From 5943b27e3264ef5856f74822a1d6a616ebda75a0 Mon Sep 17 00:00:00 2001 From: Maximiliano Curia Date: Sun, 19 Jun 2016 08:43:12 +0100 Subject: [PATCH] Import kldap_16.04.2.orig.tar.xz [dgit import orig kldap_16.04.2.orig.tar.xz] --- .arcconfig | 4 + .reviewboardrc | 5 + CMakeLists.txt | 87 ++ COPYING.LIB | 510 ++++++++++++ KF5LdapConfig.cmake.in | 21 + Mainpage.dox | 22 + autotests/CMakeLists.txt | 7 + autotests/testkldap.cpp | 424 ++++++++++ autotests/testkldap.h | 57 ++ autotests/testurl.txt.tmpl | 1 + cmake/CMakeLists.txt | 5 + cmake/COPYING-CMAKE-SCRIPTS | 22 + cmake/FindLdap.cmake | 114 +++ cmake/FindSasl2.cmake | 113 +++ metainfo.yaml | 14 + src/CMakeLists.txt | 128 +++ src/Messages.sh | 3 + src/ber.cpp | 451 +++++++++++ src/ber.h | 129 +++ src/kldap_config.h.cmake | 13 + src/ldapattributeproxymodel.cpp | 172 ++++ src/ldapattributeproxymodel.h | 99 +++ src/ldapconfigwidget.cpp | 894 +++++++++++++++++++++ src/ldapconfigwidget.h | 292 +++++++ src/ldapconnection.cpp | 430 ++++++++++ src/ldapconnection.h | 146 ++++ src/ldapcontrol.cpp | 158 ++++ src/ldapcontrol.h | 118 +++ src/ldapdefs.h | 161 ++++ src/ldapdn.cpp | 210 +++++ src/ldapdn.h | 89 +++ src/ldapmodel.cpp | 319 ++++++++ src/ldapmodel.h | 217 +++++ src/ldapmodel_p.cpp | 212 +++++ src/ldapmodel_p.h | 121 +++ src/ldapmodelnode_p.cpp | 132 +++ src/ldapmodelnode_p.h | 151 ++++ src/ldapobject.cpp | 151 ++++ src/ldapobject.h | 117 +++ src/ldapoperation.cpp | 1324 +++++++++++++++++++++++++++++++ src/ldapoperation.h | 300 +++++++ src/ldapsearch.cpp | 352 ++++++++ src/ldapsearch.h | 153 ++++ src/ldapserver.cpp | 396 +++++++++ src/ldapserver.h | 294 +++++++ src/ldapstructureproxymodel.cpp | 170 ++++ src/ldapstructureproxymodel.h | 98 +++ src/ldapurl.cpp | 299 +++++++ src/ldapurl.h | 185 +++++ src/ldif.cpp | 453 +++++++++++ src/ldif.h | 203 +++++ src/w32-ldap-help.h | 132 +++ 52 files changed, 10678 insertions(+) create mode 100644 .arcconfig create mode 100644 .reviewboardrc create mode 100644 CMakeLists.txt create mode 100644 COPYING.LIB create mode 100644 KF5LdapConfig.cmake.in create mode 100644 Mainpage.dox create mode 100644 autotests/CMakeLists.txt create mode 100644 autotests/testkldap.cpp create mode 100644 autotests/testkldap.h create mode 100644 autotests/testurl.txt.tmpl create mode 100644 cmake/CMakeLists.txt create mode 100644 cmake/COPYING-CMAKE-SCRIPTS create mode 100644 cmake/FindLdap.cmake create mode 100644 cmake/FindSasl2.cmake create mode 100644 metainfo.yaml create mode 100644 src/CMakeLists.txt create mode 100644 src/Messages.sh create mode 100644 src/ber.cpp create mode 100644 src/ber.h create mode 100644 src/kldap_config.h.cmake create mode 100644 src/ldapattributeproxymodel.cpp create mode 100644 src/ldapattributeproxymodel.h create mode 100644 src/ldapconfigwidget.cpp create mode 100644 src/ldapconfigwidget.h create mode 100644 src/ldapconnection.cpp create mode 100644 src/ldapconnection.h create mode 100644 src/ldapcontrol.cpp create mode 100644 src/ldapcontrol.h create mode 100644 src/ldapdefs.h create mode 100644 src/ldapdn.cpp create mode 100644 src/ldapdn.h create mode 100644 src/ldapmodel.cpp create mode 100644 src/ldapmodel.h create mode 100644 src/ldapmodel_p.cpp create mode 100644 src/ldapmodel_p.h create mode 100644 src/ldapmodelnode_p.cpp create mode 100644 src/ldapmodelnode_p.h create mode 100644 src/ldapobject.cpp create mode 100644 src/ldapobject.h create mode 100644 src/ldapoperation.cpp create mode 100644 src/ldapoperation.h create mode 100644 src/ldapsearch.cpp create mode 100644 src/ldapsearch.h create mode 100644 src/ldapserver.cpp create mode 100644 src/ldapserver.h create mode 100644 src/ldapstructureproxymodel.cpp create mode 100644 src/ldapstructureproxymodel.h create mode 100644 src/ldapurl.cpp create mode 100644 src/ldapurl.h create mode 100644 src/ldif.cpp create mode 100644 src/ldif.h create mode 100644 src/w32-ldap-help.h diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 0000000..20d53ee --- /dev/null +++ b/.arcconfig @@ -0,0 +1,4 @@ +{ + "phabricator.uri" : "https://phabricator.kde.org/project/profile/34/", + "history.immutable" : true +} diff --git a/.reviewboardrc b/.reviewboardrc new file mode 100644 index 0000000..6bbbd89 --- /dev/null +++ b/.reviewboardrc @@ -0,0 +1,5 @@ +REVIEWBOARD_URL = "https://git.reviewboard.kde.org" +REPOSITORY = "git://anongit.kde.org/kldap" +BRANCH = "master" +TARGET_GROUPS = "kdepimlibs" +TARGET_PEOPLE = "mlaurent" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0cfd988 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(KLdap) + +# ECM setup +find_package(ECM 5.19.0 CONFIG REQUIRED) +set(CMAKE_MODULE_PATH ${KLdap_SOURCE_DIR}/cmake ${ECM_MODULE_PATH}) + +include(GenerateExportHeader) +include(ECMGenerateHeaders) +include(ECMGeneratePriFile) +include(ECMPackageConfigHelpers) +include(ECMSetupVersion) +include(FeatureSummary) +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(ECMQtDeclareLoggingCategory) + +set(KF5_VERSION "5.19.0") +set(KLDAP_LIB_VERSION "5.2.2") + +ecm_setup_version(${KLDAP_LIB_VERSION} VARIABLE_PREFIX KLDAP + VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kldap_version.h" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfigVersion.cmake" + SOVERSION 5 +) + +########### Find packages ########### +find_package(KF5Completion ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5WidgetsAddons ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5I18n ${KF5_VERSION} CONFIG REQUIRED) + + +find_package(Ldap) +set_package_properties(Ldap PROPERTIES + TYPE RECOMMENDED + PURPOSE "Needed to provide LDAP functionality in KDE" +) + +find_package(Sasl2) +set_package_properties(Sasl2 PROPERTIES TYPE OPTIONAL) + +if (Ldap_FOUND) + set(LDAP_FOUND 1) +endif() + +if (Sasl2_FOUND) + set(SASL2_FOUND 1) +endif() + +add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII") +add_definitions(-DTRANSLATION_DOMAIN=\"libkldap5\") + +########### CMake Config Files ########### +set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Ldap") + +ecm_configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/KF5LdapConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfig.cmake" + INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfigVersion.cmake" + DESTINATION "${CMAKECONFIG_INSTALL_DIR}" + COMPONENT Devel +) + +install(EXPORT KF5LdapTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5LdapTargets.cmake NAMESPACE KF5::) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kldap_version.h + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} + COMPONENT Devel +) + +########### Targets ########### +add_subdirectory(cmake) +add_subdirectory(src) + +if(BUILD_TESTING) + add_subdirectory(autotests) +endif() + +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..2d2d780 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/KF5LdapConfig.cmake.in b/KF5LdapConfig.cmake.in new file mode 100644 index 0000000..057d7f6 --- /dev/null +++ b/KF5LdapConfig.cmake.in @@ -0,0 +1,21 @@ +@PACKAGE_INIT@ + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH}) + +find_dependency(Ldap) +find_dependency(Sasl2) + +include(FeatureSummary) + +set_package_properties(Ldap PROPERTIES + DESCRIPTION "LDAP (Lightweight Directory Access Protocol) libraries" + URL "http://www.openldap.org" + PURPOSE "Needed to provide LDAP functionality in KDE" +) + +set_package_properties(Sasl2 PROPERTIES + DESCRIPTION "The Cyrus-sasl library" + URL "http://www.cyrussasl.org" +) + +include("${CMAKE_CURRENT_LIST_DIR}/KF5LdapTargets.cmake") diff --git a/Mainpage.dox b/Mainpage.dox new file mode 100644 index 0000000..1de92ec --- /dev/null +++ b/Mainpage.dox @@ -0,0 +1,22 @@ +/*! + * @mainpage kldap - an LDAP access API for KDE. + * + * @section purpose Purpose + * + * LIBKLDAP + * + * @section desc Description + * + * Allows LDAP accessing with a convenient Qt style C++ API. + * + * @authors György Szombathelyi \ + * + * @maintainers György Szombathelyi \ + * + * @licenses + * @lgpl + */ + +// DOXYGEN_PROJECTNAME=KLDAP Library +// DOXYGEN_REFERENCES = kdecore kdeui +// DOXYGEN_EXCLUDE = scripts diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt new file mode 100644 index 0000000..1ac6183 --- /dev/null +++ b/autotests/CMakeLists.txt @@ -0,0 +1,7 @@ +include(ECMAddTests) + +find_package(Qt5Test CONFIG REQUIRED) + +if(Ldap_FOUND) + ecm_add_tests(testkldap.cpp NAME_PREFIX "kldap-" LINK_LIBRARIES KF5::Ldap Qt5::Test) +endif() diff --git a/autotests/testkldap.cpp b/autotests/testkldap.cpp new file mode 100644 index 0000000..3fa2230 --- /dev/null +++ b/autotests/testkldap.cpp @@ -0,0 +1,424 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "testkldap.h" + +#include "ldif.h" +#include "ldapdn.h" +#include "ldapurl.h" +#include "ldapserver.h" +#include "ldapconnection.h" +#include "ldapmodel.h" +#include "ldapoperation.h" +#include "ldapsearch.h" +#include "ber.h" + +#include +#include +#include +QTEST_MAIN(KLdapTest) + +void KLdapTest::initTestCase() +{ + /* + Read in the connection details of an LDAP server to use for testing. + You should copy the file testurl.txt.tmpl to testurl.txt and specify a url in this file. + The specified server should not be a production server in case we break anything here. + You have been warned! + */ + m_search = 0; + m_model = 0; + + QString filename(QStringLiteral("testurl.txt")); + QFile file(filename); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream stream(&file); + stream >> m_url; + file.close(); + } +// else +// QCOMPARE( 0, 1 ); + + m_search = new LdapSearch; + + /* Let's also create an LdapModel object */ + m_model = new LdapModel(this); +} + +void KLdapTest::testBer() +{ + Ber ber1, ber2, ber3, ber4, ber5, ber6, ber7; + Ber bber; + QByteArray flat; + + int ainteger; + QByteArray aoctetString1, aoctetString2, aoctetString3; + QList alist1, alist2; + + int binteger; + QByteArray boctetString1, boctetString2, boctetString3; + QList blist1, blist2; + + aoctetString1 = "KDE"; + aoctetString2 = "the"; + aoctetString3 = "next generation"; + + alist1.append(aoctetString1); + alist1.append(aoctetString2); + + alist2.append(aoctetString2); + alist2.append(aoctetString3); + alist2.append(aoctetString1); + + ainteger = 23543; + + ber1.printf(QStringLiteral("i"), ainteger); + ber2.printf(QStringLiteral("o"), &aoctetString1); + ber3.printf(QStringLiteral("O"), &aoctetString2); + ber4.printf(QStringLiteral("s"), &aoctetString3); + ber5.printf(QStringLiteral("{v}"), &alist1); + ber6.printf(QStringLiteral("{V}"), &alist2); + ber7.printf(QStringLiteral("oi{v}O"), &aoctetString1, ainteger, &alist2, &aoctetString2); + + //test integer: + bber = ber1; + bber.scanf(QStringLiteral("i"), &binteger); + QCOMPARE(ainteger, binteger); + + //test octet strings: + bber = ber2; + bber.scanf(QStringLiteral("o"), &boctetString1); + QCOMPARE(aoctetString1, boctetString1); + bber = ber3; + bber.scanf(QStringLiteral("o"), &boctetString2); + QCOMPARE(aoctetString2, boctetString2); + bber = ber4; + bber.scanf(QStringLiteral("o"), &boctetString3); + QCOMPARE(aoctetString3, boctetString3); + + //test sequence of octet strings: + bber = ber5; + bber.scanf(QStringLiteral("v"), &blist1); + QCOMPARE(alist1, blist1); + + bber = ber6; + bber.scanf(QStringLiteral("v"), &blist2); + QCOMPARE(alist2, blist2); + + //complex tests + boctetString1 = boctetString2 = boctetString3 = QByteArray(); + binteger = 0; + blist1.clear(); + blist2.clear(); + + bber = ber7; + bber.scanf(QStringLiteral("oivO"), &boctetString1, &binteger, &blist2, &boctetString2); + QCOMPARE(aoctetString1, boctetString1); + QCOMPARE(aoctetString2, boctetString2); + QCOMPARE(alist2, blist2); + QCOMPARE(ainteger, binteger); +} + +void KLdapTest::cleanupTestCase() +{ + delete m_search; + delete m_model; +} + +void KLdapTest::testLdapUrl() +{ + // Test LdapUrl using some hardwired values so that we know what to compare to + LdapUrl url; + bool critical; + + url.setUrl(QStringLiteral("ldap://cn=manager,dc=kde,dc=org:password@localhost:3999/" + "dc=kde,dc=org?cn,mail?sub?(objectClass=*)?x-dir=base")); + url.parseQuery(); + + QCOMPARE(url.userName(), QString::fromLatin1("cn=manager,dc=kde,dc=org")); + QCOMPARE(url.password(), QString::fromLatin1("password")); + QCOMPARE(url.dn(), LdapDN(QStringLiteral("dc=kde,dc=org"))); + QCOMPARE(url.scope(), LdapUrl::Sub); + QCOMPARE(url.attributes().at(0), QString::fromLatin1("cn")); + QCOMPARE(url.attributes().at(1), QString::fromLatin1("mail")); + QCOMPARE(url.filter(), QString::fromLatin1("(objectClass=*)")); + QCOMPARE(url.extension(QString::fromLatin1("x-dir"), critical), + QString::fromLatin1("base")); +} + +void KLdapTest::testLdapConnection() +{ + // Try to connect using an LdapUrl (read in from testurl.txt). + LdapUrl url; + url.setUrl(m_url); + + LdapConnection conn; + conn.setUrl(url); + int ret; + if ((ret = conn.connect())) { + qDebug() << "Could not connect to LDAP server. Error was:" << conn.connectionError(); + } + QCOMPARE(ret, 0); + + LdapOperation op(conn); + // Now attempt to bind + if ((ret = op.bind_s())) { + qDebug() << "Could not bind to server. Error was:" << conn.ldapErrorString(); + } + QEXPECT_FAIL("", "Will fail since no server is available for testing", Abort); + QCOMPARE(ret, 0); +} + +void KLdapTest::testLdapSearch() +{ + // Lets try a search using the specified url + LdapUrl url; + url.setUrl(m_url); + url.parseQuery(); + connect(m_search, SIGNAL(result(KLDAP::LdapSearch*)), + this, SLOT(searchResult(KLDAP::LdapSearch*))); + connect(m_search, SIGNAL(data(KLDAP::LdapSearch*,KLDAP::LdapObject)), + this, SLOT(searchData(KLDAP::LdapSearch*,KLDAP::LdapObject))); + bool success = m_search->search(url); + while (QCoreApplication::hasPendingEvents()) { + qApp->processEvents(); + } + + QEXPECT_FAIL("", "Will fail since no server is available for testing", Abort); + QCOMPARE(success, true); + + qDebug() << "Search found" << m_objects.size() << "matching entries"; +} + +void KLdapTest::searchResult(KLDAP::LdapSearch *search) +{ + qDebug(); + int err = search->error(); + if (err) { + qDebug() << "Search returned the following error:" << search->errorString(); + } + QCOMPARE(err, 0); +} + +void KLdapTest::searchData(KLDAP::LdapSearch *search, const KLDAP::LdapObject &obj) +{ + Q_UNUSED(search); + //qDebug(); + //qDebug() << "Object:"; + //qDebug() << obj.toString(); + m_objects.append(obj); +} + +void KLdapTest::testLdapDN() +{ + QString strDN(QStringLiteral("uid=Test\\+Person+ou=accounts\\,outgoing,dc=kde,dc=org")); + LdapDN dn(strDN); + QCOMPARE(dn.isValid(), true); + QCOMPARE(dn.rdnString(), QStringLiteral("uid=Test\\+Person+ou=accounts\\,outgoing")); +} + +void KLdapTest::testLdapModel() +{ + // Use the user-supplied testing url + LdapUrl url; + url.setUrl(m_url); + + // Create a connection to use and bind with it + LdapConnection conn; + conn.setUrl(url); + int ret; + if ((ret = conn.connect())) { + qDebug() << "Could not connect to LDAP server. Error was:" << conn.connectionError(); + } + QCOMPARE(ret, 0); + + LdapOperation op(conn); + if ((ret = op.bind_s())) { + qDebug() << "Could not bind to server. Error was:" << conn.ldapErrorString(); + } + QEXPECT_FAIL("", "Will fail since no server is available for testing", Abort); + QCOMPARE(ret, 0); + + // Let's use this connection with the model + m_model->setConnection(conn); + + while (QCoreApplication::hasPendingEvents()) { + qApp->processEvents(); + } + + QModelIndex rootIndex = QModelIndex(); + QVariant data = m_model->data(rootIndex, Qt::DisplayRole); + qDebug() << "Root Item Distinguished Name =" << data.toString(); + + QVERIFY(m_model->hasChildren(rootIndex) == true); + QVERIFY(m_model->canFetchMore(rootIndex) == false); +} + +/* + void KLdapTest::testKLdap() + { + LdapUrl url; + bool critical; + + url.setUrl("ldap://cn=manager,dc=kde,dc=org:password@localhost:3999" + "/dc=kde,dc=org?cn,mail?sub?(objectClass=*)?x-dir=base"); + url.parseQuery(); + + QCOMPARE( url.user(), QString::fromLatin1("cn=manager,dc=kde,dc=org") ); + QCOMPARE( url.password(), QString::fromLatin1("password") ); + QCOMPARE( url.dn(), QString::fromLatin1("dc=kde,dc=org") ); + QCOMPARE( url.scope(), LdapUrl::Sub ); + QCOMPARE( url.attributes().at(0), QString::fromLatin1("cn") ); + QCOMPARE( url.attributes().at(1), QString::fromLatin1("mail") ); + QCOMPARE( url.filter(), QString::fromLatin1("(objectClass=*)") ); + QCOMPARE( url.extension(QString::fromLatin1("x-dir"), critical), QString::fromLatin1("base") ); + + url.setDn("ou=People,dc=kde,dc=org"); + QCOMPARE( url.dn(), QString::fromLatin1("ou=People,dc=kde,dc=org") ); + url.setDn("/ou=People,dc=kde,dc=org"); + QCOMPARE( url.dn(), QString::fromLatin1("ou=People,dc=kde,dc=org") ); + + LdapServer server; +// url.setUrl("ldaps://cn=manager,dc=kde,dc=org:passwor@localhost:3999/" + "dc=kde,dc=org????x-timelimt=5,x-sizelimit=6,x=pagesize=7,binddn=cn=apple,ou=berry"); +url.setUrl("ldaps://cn=manager,dc=kde,dc=org:password@localhost:3999/" + "dc=kde,dc=org??base??x-timelimit=5"); +url.parseQuery(); +server.setUrl( url ); +QCOMPARE( url.query(), QString::fromLatin1("??base??x-timelimit=5") ); +QCOMPARE( url.url(), server.url().url() ); + +LdapControl c1; +c1.setControl( QString::fromLatin1("1.2.3.4.5.6"), QByteArray("abcdefg"), true ); +//test copy constructor +LdapControl c2(c1); +QCOMPARE( c2.oid(), QString::fromLatin1("1.2.3.4.5.6") ); +QCOMPARE( c2.value(), QByteArray("abcdefg") ); +QCOMPARE( c2.critical(), true ); +//test assignment operator +LdapControl c3; +c3 = c1; +QCOMPARE( c3.oid(), QString::fromLatin1("1.2.3.4.5.6") ); +QCOMPARE( c3.value(), QByteArray("abcdefg") ); +QCOMPARE( c3.critical(), true ); +*/ +//test Ber functions +/* + QByteArray left1("bertest"), right1; + int left2 = 0, right2; + int left3 = 1, right3; + int left4 = 2, right4; + int left5 = 3, right5; + int left6 = 1, right6; + QList left7, right7; + left7.append( "abcdefghij" ); + left7.append( "123456789" ); + left7.append( "1234\0\0\056789" ); + + Ber ber; + ber.printf("{seeiib}", &left1, left2, left3, left4, left5, left6 ); + +// ber.printf("{ioOi{i}}", left3, &left1, &left2, left4, left4 ); +Ber ber2 = ber; + +unsigned int a; +int b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; +a = ber2.skipTag( b ); +qDebug() << "next tag:" << a << "size:" << b; + +BerElement *_ber, *_ber2; +_ber = ber_alloc_t( LBER_USE_DER ); + +ber_len_t bl; +ber_printf( _ber, "{i}", 5 ); +qDebug() << "native"; +_ber2 = ber_dup( _ber ); +a = ber_skip_tag( _ber2, &bl ); +qDebug() << "next tag:" << a << "size:" << bl; +// ber_dump( _ber, 0 ); + +// ber2.scanf("{v}", &right5 ); + +// ber2.scanf("{inoOi{v}}", &right3, &right1, &right2, &right4, &right5 ); + +// QCOMPARE( left1, right1 ); +// QCOMPARE( left2, right2 ); +// QCOMPARE( left3, right3 ); +// QCOMPARE( left4, right4 ); +// QCOMPARE( left5, right5 ); +*/ +/* + url.setUrl("ldap://localhost/dc=gyurco,dc=localdomain"); + url.parseQuery(); + server.setUrl( url ); + LdapConnection conn( server ); + int result = conn.connect(); + qDebug() << "connect result" << result << conn.errorString(); + + LdapOperation op( conn ); + int msgid = op.search( "ou=People,dc=gyurco,dc=localdomain", LdapUrl::One, "", QStringList() ); + qDebug() << "search msgid" << msgid; + result = op.result( msgid ); + qDebug() << "error code" << conn.ldapErrorCode() << "str:" << conn.ldapErrorString(); + while ( result == LdapOperation::RES_SEARCH_ENTRY ) { + qDebug() << op.object().toString(); + result = op.result( msgid ); + } + qDebug() << "error code" << conn.ldapErrorCode() << "str:" << conn.ldapErrorString(); + + msgid = op.del( "ou=People,dc=gyurco,dc=localdomain" ); + qDebug() << "search msgid" << msgid; + result = op.result( msgid ); + qDebug() << "error code" << conn.ldapErrorCode() << "str:" << conn.ldapErrorString(); + + msgid = op.compare( "ou=People,dc=gyurco,dc=localdomain", "objectClass", QByteArray("top") ); + qDebug() << "search msgid" << msgid; + result = op.result( msgid ); + qDebug() << "error code" << conn.ldapErrorCode() << "str:" << conn.ldapErrorString(); + + msgid = op.compare( "ou=People,dc=gyurco,dc=localdomain", "objectClass", + QByteArray("inetOrgPerson") ); + qDebug() << "search msgid" << msgid; + result = op.result( msgid ); + qDebug() << "error code" << conn.ldapErrorCode() << "str:" << conn.ldapErrorString(); + + msgid = op.exop( "1.2.3.4.5.6.7.8", QByteArray("inetOrgPerson") ); + qDebug() << "search msgid" << msgid; + result = op.result( msgid ); + qDebug() << "error code" << conn.ldapErrorCode() << "str:" << conn.ldapErrorString(); +*/ +/* + } +*/ diff --git a/autotests/testkldap.h b/autotests/testkldap.h new file mode 100644 index 0000000..944e20c --- /dev/null +++ b/autotests/testkldap.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2006 Volker Krause + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef TESTKLDAP_H +#define TESTKLDAP_H + +#include +#include +#include +#include + +using namespace KLDAP; + +class KLdapTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + //void testKLdap(); + + void initTestCase(); + void cleanupTestCase(); + + void testLdapUrl(); + void testBer(); + void testLdapConnection(); + void testLdapSearch(); + void testLdapDN(); + void testLdapModel(); + +public Q_SLOTS: + void searchResult(KLDAP::LdapSearch *search); + void searchData(KLDAP::LdapSearch *search, const KLDAP::LdapObject &obj); + +private: + QString m_url; + LdapSearch *m_search; + LdapObjects m_objects; + LdapModel *m_model; +}; + +#endif diff --git a/autotests/testurl.txt.tmpl b/autotests/testurl.txt.tmpl new file mode 100644 index 0000000..2cdc6a8 --- /dev/null +++ b/autotests/testurl.txt.tmpl @@ -0,0 +1 @@ +ldap://cn=admin,dc=kde,dc=org:password@ldap.kde.org:389/ou=people,dc=kde,dc=org?cn,mail,loginShell?sub?(uid=*) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 0000000..55201c6 --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1,5 @@ +install(FILES + FindLdap.cmake + FindSasl2.cmake + DESTINATION ${CMAKECONFIG_INSTALL_DIR} +) diff --git a/cmake/COPYING-CMAKE-SCRIPTS b/cmake/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/cmake/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/FindLdap.cmake b/cmake/FindLdap.cmake new file mode 100644 index 0000000..7f8ef04 --- /dev/null +++ b/cmake/FindLdap.cmake @@ -0,0 +1,114 @@ +#.rst: +# FindLdap +# -------- +# +# Try to find the LDAP client libraries. +# +# This will define the following variables: +# +# ``Ldap_FOUND`` +# True if libldap is available. +# +# ``Ldap_VERSION`` +# The version of libldap +# +# ``Ldap_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if +# the target is not used for linking +# +# ``Ldap_LIBRARIES`` +# The LDAP libraries (libldap + liblber if available) +# This can be passed to target_link_libraries() instead of +# the ``Ldap::Ldap`` target +# +# If ``Ldap_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``Ldap::Ldap`` +# The LDAP library +# +# Since pre-5.0.0. +# +# Imported target since 5.1.41 +# +#============================================================================= +# Copyright 2006 Szombathelyi György +# Copyright 2007-2016 Laurent Montel +# +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +find_path(Ldap_INCLUDE_DIRS NAMES ldap.h) + +if(APPLE) + find_library(Ldap_LIBRARIES NAMES LDAP + PATHS + /System/Library/Frameworks + /Library/Frameworks + ) +else() + find_library(Ldap_LIBRARIES NAMES ldap) + find_library(Lber_LIBRARIES NAMES lber) +endif() + +if(Ldap_LIBRARIES AND Lber_LIBRARIES) + set(Ldap_LIBRARIES ${Ldap_LIBRARIES} ${Lber_LIBRARIES}) +endif() + +if(EXISTS ${Ldap_INCLUDE_DIRS}/ldap_features.h) + file(READ ${Ldap_INCLUDE_DIRS}/ldap_features.h LDAP_FEATURES_H_CONTENT) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_MAJOR[ ]+[0-9]+" _LDAP_VERSION_MAJOR_MATCH ${LDAP_FEATURES_H_CONTENT}) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_MINOR[ ]+[0-9]+" _LDAP_VERSION_MINOR_MATCH ${LDAP_FEATURES_H_CONTENT}) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_PATCH[ ]+[0-9]+" _LDAP_VERSION_PATCH_MATCH ${LDAP_FEATURES_H_CONTENT}) + + string(REGEX REPLACE ".*_MAJOR[ ]+(.*)" "\\1" LDAP_VERSION_MAJOR ${_LDAP_VERSION_MAJOR_MATCH}) + string(REGEX REPLACE ".*_MINOR[ ]+(.*)" "\\1" LDAP_VERSION_MINOR ${_LDAP_VERSION_MINOR_MATCH}) + string(REGEX REPLACE ".*_PATCH[ ]+(.*)" "\\1" LDAP_VERSION_PATCH ${_LDAP_VERSION_PATCH_MATCH}) + + set(Ldap_VERSION "${LDAP_VERSION_MAJOR}.${LDAP_VERSION_MINOR}.${LDAP_VERSION_PATCH}") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Ldap + FOUND_VAR Ldap_FOUND + REQUIRED_VARS Ldap_LIBRARIES Ldap_INCLUDE_DIRS + VERSION_VAR Ldap_VERSION +) + +if(Ldap_FOUND AND NOT TARGET Ldap::Ldap) + add_library(Ldap::Ldap UNKNOWN IMPORTED) + set_target_properties(Ldap::Ldap PROPERTIES + IMPORTED_LOCATION "${Ldap_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Ldap_INCLUDE_DIRS}") +endif() + +mark_as_advanced(Ldap_INCLUDE_DIRS Ldap_LIBRARIES Lber_LIBRARIES Ldap_VERSION) + +include(FeatureSummary) +set_package_properties(Ldap PROPERTIES + URL "http://www.openldap.org/" + DESCRIPTION "LDAP (Lightweight Directory Access Protocol) libraries." +) diff --git a/cmake/FindSasl2.cmake b/cmake/FindSasl2.cmake new file mode 100644 index 0000000..47b13a5 --- /dev/null +++ b/cmake/FindSasl2.cmake @@ -0,0 +1,113 @@ +#.rst: +# FindSasl2 +# --------- +# +# Try to find the SASL2 library. +# +# This will define the following variables: +# +# ``Sasl2_FOUND`` +# System has SASL2. +# +# ``Sasl2_VERSION`` +# The version of SASL2. +# +# ``Sasl2_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if +# the target is not used for linking. +# +# ``Sasl2_LIBRARIES`` +# The SASL2 library. +# This can be passed to target_link_libraries() instead of +# the ``Sasl2::Sasl2`` target +# +# If ``Sasl2_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``Sasl2::Sasl2`` +# The SASL2 library +# +# Since pre-5.0.0. +# +# Imported target since 5.1.41 +# +#============================================================================= +# Copyright 2006, 2007 Laurent Montel +# +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# NOTE: libsasl2.pc doesn't export the include dir. +find_package(PkgConfig QUIET) +pkg_check_modules(PC_Sasl2 libsasl2) + +find_path(Sasl2_INCLUDE_DIRS NAMES sasl/sasl.h) + +# libsasl2 add for windows, because the windows package of cyrus-sasl2 +# contains a libsasl2 also for msvc which is not standard conform +find_library(Sasl2_LIBRARIES + NAMES sasl2 libsasl2 + HINTS ${PC_Sasl2_LIBRARY_DIRS} +) + +set(Sasl2_VERSION ${PC_Sasl2_VERSION}) + +if(NOT Sasl2_VERSION) + if(EXISTS ${Sasl2_INCLUDE_DIRS}/sasl/sasl.h) + file(READ ${Sasl2_INCLUDE_DIRS}/sasl/sasl.h SASL2_H_CONTENT) + string(REGEX MATCH "#define SASL_VERSION_MAJOR[ ]+[0-9]+" SASL2_VERSION_MAJOR_MATCH ${SASL2_H_CONTENT}) + string(REGEX MATCH "#define SASL_VERSION_MINOR[ ]+[0-9]+" SASL2_VERSION_MINOR_MATCH ${SASL2_H_CONTENT}) + string(REGEX MATCH "#define SASL_VERSION_STEP[ ]+[0-9]+" SASL2_VERSION_STEP_MATCH ${SASL2_H_CONTENT}) + + string(REGEX REPLACE ".*_MAJOR[ ]+(.*)" "\\1" SASL2_VERSION_MAJOR ${SASL2_VERSION_MAJOR_MATCH}) + string(REGEX REPLACE ".*_MINOR[ ]+(.*)" "\\1" SASL2_VERSION_MINOR ${SASL2_VERSION_MINOR_MATCH}) + string(REGEX REPLACE ".*_STEP[ ]+(.*)" "\\1" SASL2_VERSION_STEP ${SASL2_VERSION_STEP_MATCH}) + + set(Sasl2_VERSION "${SASL2_VERSION_MAJOR}.${SASL2_VERSION_MINOR}.${SASL2_VERSION_STEP}") + endif() +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Sasl2 + FOUND_VAR Sasl2_FOUND + REQUIRED_VARS Sasl2_LIBRARIES Sasl2_INCLUDE_DIRS + VERSION_VAR Sasl2_VERSION +) +if(Sasl2_FOUND AND NOT TARGET Sasl2::Sasl2) + add_library(Sasl2::Sasl2 UNKNOWN IMPORTED) + set_target_properties(Sasl2::Sasl2 PROPERTIES + IMPORTED_LOCATION "${Sasl2_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Sasl2_INCLUDE_DIRS}") +endif() + +mark_as_advanced(Sasl2_LIBRARIES Sasl2_INCLUDE_DIRS Sasl2_VERSION) + +include(FeatureSummary) +set_package_properties(Sasl2 PROPERTIES + URL "http://www.cyrussasl.org/" + DESCRIPTION "The Cyrus-sasl library." +) + diff --git a/metainfo.yaml b/metainfo.yaml new file mode 100644 index 0000000..d5022dd --- /dev/null +++ b/metainfo.yaml @@ -0,0 +1,14 @@ +maintainer: mlaurent +description: LDap support library +tier: 3 +type: functional +platforms: + - name: All +portingAid: false +deprecated: false +release: false +libraries: + - qmake: Ldap + cmake: "KF5::Ldap" +cmakename: KF5Ldap + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..ad81162 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,128 @@ +include(CheckFunctionExists) +include(CheckIncludeFiles) +include(CheckSymbolExists) + +# Reactivate it +remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) + +check_include_files(sys/time.h HAVE_SYS_TIME_H) + +set(kldap_EXTRA_LIBS) + +if(Ldap_FOUND) + set(kldap_EXTRA_LIBS ${Ldap_LIBRARIES}) + if(WIN32) + set(kldap_EXTRA_LIBS ${kldap_EXTRA_LIBS} ws2_32) + endif() + set(HAVE_LDAP_H) + set(CMAKE_REQUIRED_INCLUDES lber.h ldap.h) + set(CMAKE_REQUIRED_LIBRARIES ${Ldap_LIBRARIES}) + check_function_exists(ldap_start_tls_s HAVE_LDAP_START_TLS_S) + check_function_exists(ldap_initialize HAVE_LDAP_INITIALIZE) + check_function_exists(ber_memfree HAVE_BER_MEMFREE) + check_function_exists(ldap_unbind_ext HAVE_LDAP_UNBIND_EXT) + check_function_exists(ldap_extended_operation HAVE_LDAP_EXTENDED_OPERATION) + check_function_exists(ldap_extended_operation_s HAVE_LDAP_EXTENDED_OPERATION_S) + check_symbol_exists(ldap_extended_operation ldap.h HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE) + check_symbol_exists(ldap_extended_operation_s ldap.h HAVE_LDAP_EXTENDED_OPERATION_S_PROTOTYPE) +endif() + +if(Sasl2_FOUND) + set(kldap_EXTRA_LIBS ${kldap_EXTRA_LIBS} ${Sasl2_LIBRARIES}) +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kldap_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kldap_config.h) + +########### next target ############### + +set(kldap_LIB_SRCS + ber.cpp + ldif.cpp + ldapurl.cpp + ldapserver.cpp + ldapobject.cpp + ldapconnection.cpp + ldapoperation.cpp + ldapcontrol.cpp + ldapsearch.cpp + ldapconfigwidget.cpp + ldapdn.cpp + ldapmodelnode_p.cpp + ldapmodel.cpp + ldapmodel_p.cpp + ldapstructureproxymodel.cpp + ldapattributeproxymodel.cpp +) +ecm_qt_declare_logging_category(kldap_LIB_SRCS HEADER ldap_debug.h IDENTIFIER LDAP_LOG CATEGORY_NAME log_ldap) + +add_library(KF5Ldap ${kldap_LIB_SRCS}) + +generate_export_header(KF5Ldap BASE_NAME kldap) + +add_library(KF5::Ldap ALIAS KF5Ldap) + +target_link_libraries(KF5Ldap +PRIVATE + KF5::Completion + Qt5::Widgets + KF5::I18n + KF5::WidgetsAddons + ${kldap_EXTRA_LIBS} +) + +target_include_directories(KF5Ldap INTERFACE "$") +target_include_directories(KF5Ldap PUBLIC "$") + +if(Sasl2_FOUND) + target_include_directories(KF5Ldap INTERFACE "$") + target_include_directories(KF5Ldap PUBLIC "$") +endif() + +set_target_properties(KF5Ldap PROPERTIES + VERSION ${KLDAP_VERSION_STRING} + SOVERSION ${KLDAP_SOVERSION} + EXPORT_NAME Ldap +) + + +install(TARGETS KF5Ldap EXPORT KF5LdapTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) + +########### install files ############### + +ecm_generate_headers(KLdap_CamelCase_HEADERS + HEADER_NAMES + Ber + LdapAttributeProxyModel + LdapConfigWidget + LdapConnection + LdapControl + LdapDN + LdapModel + LdapObject + LdapOperation + LdapSearch + LdapServer + LdapDefs + LdapStructureProxyModel + LdapUrl + Ldif + PREFIX KLDAP + REQUIRED_HEADERS KLdap_HEADERS +) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kldap_export.h + ${KLdap_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KLDAP/kldap + COMPONENT Devel +) + +install(FILES + ${KLdap_CamelCase_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KLDAP/KLDAP/ + COMPONENT Devel +) + +ecm_generate_pri_file(BASE_NAME Ldap LIB_NAME KF5Ldap FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KLDAP/) +install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) + diff --git a/src/Messages.sh b/src/Messages.sh new file mode 100644 index 0000000..18480f6 --- /dev/null +++ b/src/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$XGETTEXT *.cpp -o $podir/libkldap5.pot + diff --git a/src/ber.cpp b/src/ber.cpp new file mode 100644 index 0000000..2af6b96 --- /dev/null +++ b/src/ber.cpp @@ -0,0 +1,451 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ber.h" +#include "kldap_config.h" + +#include "ldap_debug.h" + +#include +#include + +#include + +#ifdef LDAP_FOUND + +#ifdef Q_OS_SOLARIS //krazy:exclude=cpp +#define BC31 1 +#endif + +#ifndef HAVE_WINLDAP_H +#include +#include +#else +#include +#endif + +#ifndef LBER_USE_DER +#define LBER_USE_DER 1 +#endif + +#ifndef HAVE_BER_MEMFREE +# ifndef HAVE_WINLDAP_H +# define ber_memfree(x) ldap_memfree(x) +# else +# define ber_memfree(x) win_ldap_memfree(x) +# endif +#endif + +#endif + +using namespace KLDAP; + +class Q_DECL_HIDDEN Ber::BerPrivate +{ +public: +#ifdef LDAP_FOUND + BerElement *mBer; +#endif +}; + +#ifdef LDAP_FOUND +Ber::Ber() + : d(new BerPrivate) +{ + d->mBer = ber_alloc_t(LBER_USE_DER); + Q_ASSERT(d->mBer); +} + +Ber::Ber(const QByteArray &value) + : d(new BerPrivate) +{ + struct berval bv; + bv.bv_val = (char *) value.data(); + bv.bv_len = value.size(); + d->mBer = ber_init(&bv); + Q_ASSERT(d->mBer); +} + +Ber::~Ber() +{ + ber_free(d->mBer, 1); + delete d; +} + +Ber::Ber(const Ber &that) + : d(new BerPrivate) +{ + struct berval *bv; + if (ber_flatten(that.d->mBer, &bv) == 0) { + d->mBer = ber_init(bv); + ber_bvfree(bv); + } +} + +Ber &Ber::operator=(const Ber &that) +{ + if (this == &that) { + return *this; + } + + struct berval *bv; + if (ber_flatten(that.d->mBer, &bv) == 0) { + d->mBer = ber_init(bv); + ber_bvfree(bv); + } + return *this; +} + +QByteArray Ber::flatten() const +{ + QByteArray ret; + struct berval *bv; + if (ber_flatten(d->mBer, &bv) == 0) { + ret = QByteArray(bv->bv_val, bv->bv_len); + ber_bvfree(bv); + } + return ret; +} + +int Ber::printf(QString format, ...) +{ + char fmt[2]; + va_list args; + va_start(args, format); + fmt[1] = '\0'; + + int i = 0, ret = 0; + while (i < format.length()) { + fmt[0] = format[i].toLatin1(); + i++; + switch (fmt[0]) { + case 'b': + case 'e': + case 'i': { + ber_int_t v = va_arg(args, int); + ret = ber_printf(d->mBer, fmt, v); + break; + } + case 'B': { + //FIXME: QBitArray vould be logical, but how to access the bits? + QByteArray *B = va_arg(args, QByteArray *); + int Bc = va_arg(args, int); + ret = ber_printf(d->mBer, fmt, B->data(), Bc); + break; + } + case 'o': { + QByteArray *o = va_arg(args, QByteArray *); + ret = ber_printf(d->mBer, fmt, o->data(), o->size()); + break; + } + case 'O': { + QByteArray *O = va_arg(args, QByteArray *); + struct berval bv; + bv.bv_val = (char *) O->data(); + bv.bv_len = O->size(); + ret = ber_printf(d->mBer, fmt, &bv); + break; + } + break; + case 's': { + QByteArray *s = va_arg(args, QByteArray *); + ret = ber_printf(d->mBer, fmt, s->data()); + break; + } + break; + case 't': { + unsigned int t = va_arg(args, unsigned int); + ret = ber_printf(d->mBer, fmt, t); + break; + } + break; + case 'v': { + QList *v = va_arg(args, QList *); + QVarLengthArray l(v->count() + 1); + int j; + for (j = 0; j < v->count(); j++) { + l[j] = v->at(j).data(); + } + l[j] = Q_NULLPTR; + ret = ber_printf(d->mBer, fmt, l.data()); + break; + } + case 'V': { + QList *V = va_arg(args, QList *); + QVarLengthArray bv(V->count() + 1); + QVarLengthArray bvs(V->count()); + int j; + for (j = 0; j < V->count(); j++) { + bvs[j].bv_val = (char *) V->at(j).data(); + bvs[j].bv_len = V->at(j).size(); + bv[j] = &bvs[j]; + } + bv[V->count()] = Q_NULLPTR; + ret = ber_printf(d->mBer, fmt, bv.data()); + break; + } + case 'n': + case '{': + case '}': + case '[': + case ']': + ret = ber_printf(d->mBer, fmt); + break; + default: + qCWarning(LDAP_LOG) << "Invalid BER format parameter: '" << fmt << "'"; + ret = -1; + } + qCDebug(LDAP_LOG) << "ber_printf format:" << fmt << "ret:" << ret; + if (ret == -1) { + break; + } + } + va_end(args); + return ret; +} + +int Ber::scanf(QString format, ...) +{ + char fmt[2]; + va_list args; + va_start(args, format); + fmt[1] = '\0'; + + int i = 0, ret = 0; + while (i < format.length()) { + fmt[0] = format[i].toLatin1(); + i++; + switch (fmt[0]) { + case 'l': + case 'b': + case 'e': + case 'i': { + int *v = va_arg(args, int *); + ret = ber_scanf(d->mBer, fmt, v); + break; + } + case 'B': { + //FIXME: QBitArray vould be logical, but how to access the bits? + QByteArray *B = va_arg(args, QByteArray *); + int *Bc = va_arg(args, int *); + char *c; + ret = ber_scanf(d->mBer, fmt, &c, Bc); + if (ret != -1) { + *B = QByteArray(c, (*Bc + 7) / 8); + ber_memfree(c); + } + break; + } + case 'o': { + QByteArray *o = va_arg(args, QByteArray *); + struct berval bv; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1) { + *o = QByteArray(bv.bv_val, bv.bv_len); + ber_memfree(bv.bv_val); + } + break; + } + case 'O': { + QByteArray *O = va_arg(args, QByteArray *); + struct berval *bv; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1) { + *O = QByteArray(bv->bv_val, bv->bv_len); + ber_bvfree(bv); + } + break; + } + break; + case 'm': { //the same as 'O', just *bv should not be freed. + QByteArray *m = va_arg(args, QByteArray *); + struct berval *bv; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1) { + *m = QByteArray(bv->bv_val, bv->bv_len); + } + break; + } + case 'a': { + QByteArray *a = va_arg(args, QByteArray *); + char *c; + ret = ber_scanf(d->mBer, fmt, &c); + if (ret != -1) { + *a = QByteArray(c); + ber_memfree(c); + } + break; + } + + case 's': { + QByteArray *s = va_arg(args, QByteArray *); + char buf[255]; + ber_len_t l = sizeof(buf); + ret = ber_scanf(d->mBer, fmt, &buf, &l); + if (ret != -1) { + *s = QByteArray(buf, l); + } + break; + } + case 't': + case 'T': { + unsigned int *t = va_arg(args, unsigned int *); + ret = ber_scanf(d->mBer, fmt, t); + break; + } + break; + case 'v': { + QList *v = va_arg(args, QList *); + char **c, **c2; + ret = ber_scanf(d->mBer, fmt, &c); + if (ret != -1 && c) { + c2 = c; + while (*c) { + v->append(QByteArray(*c)); + ber_memfree(*c); + c++; + } + ber_memfree((char *) c2); + } + break; + } + case 'V': { + QList *v = va_arg(args, QList *); + struct berval **bv, **bv2; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1 && bv) { + bv2 = bv; + while (*bv) { + v->append(QByteArray((*bv)->bv_val, (*bv)->bv_len)); + bv++; + } + ber_bvecfree(bv2); + } + break; + } + case 'x': + case 'n': + case '{': + case '}': + case '[': + case ']': + ret = ber_scanf(d->mBer, fmt); + break; + default: + qCWarning(LDAP_LOG) << "Invalid BER format parameter: '" << fmt << "'"; + ret = -1; + } + + qCDebug(LDAP_LOG) << "ber_scanf format:" << fmt << "ret:" << ret; + if (ret == -1) { + break; + } + + } + va_end(args); + return ret; +} + +unsigned int Ber::peekTag(int &size) +{ + unsigned int ret; + ber_len_t len; + ret = ber_peek_tag(d->mBer, &len); + size = len; + return ret; +} + +unsigned int Ber::skipTag(int &size) +{ + unsigned int ret; + ber_len_t len; + ret = ber_skip_tag(d->mBer, &len); + size = len; + return ret; +} +#else + +Ber::Ber() + : d(new BerPrivate) +{ + qCritical() << "LDAP support not compiled"; +} + +Ber::Ber(const QByteArray &) + : d(new BerPrivate) +{ + qCritical() << "LDAP support not compiled"; +} + +Ber::~Ber() +{ + delete d; +} + +Ber::Ber(const Ber &) + : d(new BerPrivate) +{ + qCritical() << "LDAP support not compiled"; +} + +Ber &Ber::operator=(const Ber &that) +{ + if (this == &that) { + return *this; + } + qCritical() << "LDAP support not compiled"; + return *this; +} + +QByteArray Ber::flatten() const +{ + qCritical() << "LDAP support not compiled"; + return QByteArray(); +} + +int Ber::printf(const QString &format, ...) +{ + Q_UNUSED(format); + qCritical() << "LDAP support not compiled"; + return -1; +} + +int Ber::scanf(const QString &format, ...) +{ + Q_UNUSED(format); + qCritical() << "LDAP support not compiled"; + return -1; +} + +unsigned int Ber::peekTag(int &size) +{ + Q_UNUSED(size); + qCritical() << "LDAP support not compiled"; + return 0; +} + +unsigned int Ber::skipTag(int &size) +{ + Q_UNUSED(size); + qCritical() << "LDAP support not compiled"; + return 0; +} + +#endif diff --git a/src/ber.h b/src/ber.h new file mode 100644 index 0000000..62acb2a --- /dev/null +++ b/src/ber.h @@ -0,0 +1,129 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_BER_H +#define KLDAP_BER_H + +#include + +#include "kldap_export.h" + +namespace KLDAP +{ + +/** + * This class allows encoding and decoding Qt structures using Basic + * Encoding Rules. + */ +class KLDAP_EXPORT Ber +{ +public: + /** + * Constructs a Ber object. + */ + Ber(); + /** + * Constructs a Ber object from the value. + */ + explicit Ber(const QByteArray &value); + /** + * Destroys the Ber object. + */ + virtual ~Ber(); + + Ber(const Ber &that); + Ber &operator=(const Ber &that); + + /** + * Returns the Ber object as a flat QByteArray. + */ + QByteArray flatten() const; + + /** + * Appends the data with the specified format to the Ber object. + * This function works like printf, except that it's appending the + * parameters, not replacing them. The allowed format characters and + * the expected parameter types are: + *
    + *
  • + * b Boolean. An int parameter should be supplied. + * A boolean element is output. + *
  • + *
  • + * e Enumeration. An int parameter should be supplied. + * An enumeration element is output. + *
  • + *
  • + * i Integer. An int parameter should be supplied. + * An integer element is output. + *
  • + *
  • + * B Bitstring. A pointer to a QByteArray which contains the + * bitstring is supplied, followed by the number of bits in the + * bitstring. A bitstring element is output. + *
  • + *
  • + * n Null. No parameter is required. A null element is output. + *
  • + *
  • + * O,o,s Octet string. A QByteArray * is supplied. + * An octet string element is output. + * Due to versatility of Qt's QByteArray, these three format + * strings are all accepts the same parameter, but using the 's' + * format the string will be encoded only to the first zero + * character (a null terminated string)! + *
  • + *
  • + * t Tag. An int specifying the tag to give the next element + * is provided. This works across calls. + *
  • + *
  • + * v,V Several octet strings. A QList* is supplied. + * Note that a construct like ’{v}’ is required to get an actual + * SEQUENCE OF octet strings. Also note that the 'v' format recognizes + * the QByteArray only to the first zero character, so it's not + * appropriate for binary data, just only for null terminated strings! + *
  • + *
  • + * { Begin sequence. No parameter is required. + *
  • + *
  • + * } End sequence. No parameter is required. + *
  • + *
  • + * [ Begin set. No parameter is required. + *
  • + *
  • + * ] End set. No parameter is required. + *
  • + *
+ */ + int printf(QString format, ...); // Passing by-value since it's used by va_start + int scanf(QString format, ...); + unsigned int peekTag(int &size); + unsigned int skipTag(int &size); + +private: + class BerPrivate; + BerPrivate *const d; +}; + +} +#endif diff --git a/src/kldap_config.h.cmake b/src/kldap_config.h.cmake new file mode 100644 index 0000000..8abd3fa --- /dev/null +++ b/src/kldap_config.h.cmake @@ -0,0 +1,13 @@ +#cmakedefine LDAP_FOUND +#cmakedefine SASL2_FOUND +#cmakedefine HAVE_WINLDAP_H +#cmakedefine HAVE_LDAP_H +#cmakedefine HAVE_SYS_TIME_H +#cmakedefine HAVE_LDAP_START_TLS_S +#cmakedefine HAVE_LDAP_INITIALIZE +#cmakedefine HAVE_BER_MEMFREE +#cmakedefine HAVE_LDAP_UNBIND_EXT +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION_S +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION_S_PROTOTYPE diff --git a/src/ldapattributeproxymodel.cpp b/src/ldapattributeproxymodel.cpp new file mode 100644 index 0000000..4b27bcd --- /dev/null +++ b/src/ldapattributeproxymodel.cpp @@ -0,0 +1,172 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapattributeproxymodel.h" +#include "ldapmodel.h" +#include "ldapmodelnode_p.h" + +#include "ldap_debug.h" +#include + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapAttributeProxyModel::LdapAttributeProxyModelPrivate +{ +public: + LdapAttributeProxyModelPrivate(); + +}; + +LdapAttributeProxyModel::LdapAttributeProxyModelPrivate::LdapAttributeProxyModelPrivate() +{ + +} + +LdapAttributeProxyModel::LdapAttributeProxyModel(QObject *parent) + : QSortFilterProxyModel(parent), + m_d(new LdapAttributeProxyModelPrivate()) +{ + +} + +LdapAttributeProxyModel::~LdapAttributeProxyModel() +{ + delete m_d; +} + +QVariant LdapAttributeProxyModel::data(const QModelIndex &index, + int role) const +{ + // Included just in case we decide to do any special presentation of the data + // at some other point throughout the 4.x series. + return sourceModel()->data(mapToSource(index), role); +} + +bool LdapAttributeProxyModel::setData(const QModelIndex &index, + const QVariant &value, + int role) +{ + Q_UNUSED(index); + Q_UNUSED(value); + Q_UNUSED(role); + return false; +} + +bool LdapAttributeProxyModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const +{ + QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent); + LdapModelNode::NodeType nodeType = + static_cast( + sourceModel()->data(idx, LdapModel::NodeTypeRole).toUInt()); + return nodeType == LdapModelNode::Attr; +} + +QVariant LdapAttributeProxyModel::headerData(int section, + Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + if (section == 0) { + return QVariant(i18n("Attribute")); + } else if (section == 1) { + return QVariant(i18n("Value")); + } + } + + return QVariant(); +} + +int LdapAttributeProxyModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 2; +} + +Qt::ItemFlags LdapAttributeProxyModel::flags(const QModelIndex &index) const +{ + // Included so as not to break BC in case we wish to use this later in 4.x + return sourceModel()->flags(mapToSource(index)); +} + +bool LdapAttributeProxyModel::hasChildren(const QModelIndex &parent) const +{ + // We need to handle this carefully bacause of the filtering out of attributes + // and the lazy population approach. + LdapModel *model = static_cast(sourceModel()); + return model->hasChildrenOfType(mapToSource(parent), LdapModel::Attribute); +} + +QModelIndex LdapAttributeProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + return QSortFilterProxyModel::mapFromSource(sourceIndex); +} + +QModelIndex LdapAttributeProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + return QSortFilterProxyModel::mapToSource(proxyIndex); +} + +bool LdapAttributeProxyModel::insertRows(int row, int count, + const QModelIndex &parent) +{ + Q_UNUSED(row); + Q_UNUSED(count); + Q_UNUSED(parent); + return false; +} + +bool LdapAttributeProxyModel::removeRows(int row, int count, + const QModelIndex &parent) +{ + Q_UNUSED(row); + Q_UNUSED(count); + Q_UNUSED(parent); + return false; +} + +void LdapAttributeProxyModel::sort(int column, Qt::SortOrder order) +{ + Q_UNUSED(column); + Q_UNUSED(order); +} + +Qt::DropActions LdapAttributeProxyModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QMimeData *LdapAttributeProxyModel::mimeData(const QModelIndexList &indexes) const +{ + Q_UNUSED(indexes); + return Q_NULLPTR; +} + +bool LdapAttributeProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + /** \todo Implement drag and drop for LdapModel */ + Q_UNUSED(data); + Q_UNUSED(action); + Q_UNUSED(row); + Q_UNUSED(column); + Q_UNUSED(parent); + return false; +} + diff --git a/src/ldapattributeproxymodel.h b/src/ldapattributeproxymodel.h new file mode 100644 index 0000000..8a10d72 --- /dev/null +++ b/src/ldapattributeproxymodel.h @@ -0,0 +1,99 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPATTRIBUTEPROXYMODEL_H +#define KLDAP_LDAPATTRIBUTEPROXYMODEL_H + +#include + +#include "kldap_export.h" + +namespace KLDAP +{ + +class KLDAP_EXPORT LdapAttributeProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit LdapAttributeProxyModel(QObject *parent = Q_NULLPTR); + ~LdapAttributeProxyModel(); + + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::setData(). This is a placeholder for when + * LdapAttributeProxyModel beomes writeable and always returns false. + */ + bool setData(const QModelIndex &index, + const QVariant &value, + int role = Qt::EditRole) Q_DECL_OVERRIDE; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + bool hasChildren(const QModelIndex &parent) const Q_DECL_OVERRIDE; + + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const Q_DECL_OVERRIDE; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const Q_DECL_OVERRIDE; + + /** + * Reimplemented from QAbstractItemModel::insertRows(). This is a placeholder for when + * LdapAttributeProxyModel beomes writeable and always returns false. + */ + virtual bool insertRows(int row, int count, + const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::removeRows(). This is a placeholder for when + * LdapAttributeProxyModel beomes writeable and always returns false. + */ + virtual bool removeRows(int row, int count, + const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::removeRows(). The default implementation + * does nothing. + */ + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) Q_DECL_OVERRIDE; + + // + // Drag and drop support + // + /** + * Reimplemented from QAbstractItemModel::supportedDropActions(). The default + * implementation returns Qt::MoveAction. + */ + Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::mimedata(). This is a placeholder for when + * LdapAttributeProxyModel beomes writeable and always returns 0. + */ + QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::dropMimedata(). This is a placeholder for when + * LdapAttributeProxyModel beomes writeable and always returns false. + */ + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) Q_DECL_OVERRIDE; + +private: + class LdapAttributeProxyModelPrivate; + LdapAttributeProxyModelPrivate *const m_d; +}; + +} +#endif diff --git a/src/ldapconfigwidget.cpp b/src/ldapconfigwidget.cpp new file mode 100644 index 0000000..02a1555 --- /dev/null +++ b/src/ldapconfigwidget.cpp @@ -0,0 +1,894 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapconfigwidget.h" +#include "ldapsearch.h" + +#include +#include +#include "ldap_debug.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapConfigWidget::Private +{ +public: + Private(LdapConfigWidget *parent) + : mParent(parent), mFeatures(W_ALL), mProg(Q_NULLPTR) + { + mainLayout = new QGridLayout(mParent); + mainLayout->setMargin(0); + } + + void setLDAPPort(); + void setLDAPSPort(); + void setAnonymous(bool on); + void setSimple(bool on); + void setSASL(bool on); + void queryDNClicked(); + void queryMechClicked(); + void loadData(LdapSearch *search, const LdapObject &object); + void loadResult(LdapSearch *search); + void sendQuery(); + void initWidget(); + + LdapConfigWidget *mParent; + WinFlags mFeatures; + QStringList mQResult; + QString mAttr; + + QLineEdit *mUser; + QLineEdit *mPassword; + QLineEdit *mHost; + QSpinBox *mPort, *mVersion, *mSizeLimit, *mTimeLimit, *mPageSize; + QLineEdit *mDn, *mBindDn, *mRealm; + QLineEdit *mFilter; + QRadioButton *mAnonymous, *mSimple, *mSASL; + QCheckBox *mSubTree; + QPushButton *mEditButton; + QPushButton *mQueryMech; + QRadioButton *mSecNo, *mSecTLS, *mSecSSL; + KComboBox *mMech; + + bool mCancelled; + QProgressDialog *mProg; + + QGridLayout *mainLayout; +}; + +void LdapConfigWidget::Private::initWidget() +{ + QLabel *label; + + mUser = mPassword = mHost = mDn = mBindDn = mRealm = mFilter = Q_NULLPTR; + mPort = mVersion = mTimeLimit = mSizeLimit = Q_NULLPTR; + mAnonymous = mSimple = mSASL = mSecNo = mSecTLS = mSecSSL = Q_NULLPTR; + mEditButton = mQueryMech = Q_NULLPTR; + mPageSize = Q_NULLPTR; + mMech = Q_NULLPTR; + int row = 0; + int col; + + if (mFeatures & W_USER) { + label = new QLabel(i18n("User:"), mParent); + mUser = new QLineEdit(mParent); + mUser->setObjectName(QStringLiteral("kcfg_ldapuser")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mUser, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_BINDDN) { + label = new QLabel(i18n("Bind DN:"), mParent); + mBindDn = new QLineEdit(mParent); + mBindDn->setObjectName(QStringLiteral("kcfg_ldapbinddn")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mBindDn, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_REALM) { + label = new QLabel(i18n("Realm:"), mParent); + mRealm = new QLineEdit(mParent); + mRealm->setObjectName(QStringLiteral("kcfg_ldaprealm")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mRealm, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_PASS) { + label = new QLabel(i18n("Password:"), mParent); + mPassword = new QLineEdit(mParent); + mPassword->setObjectName(QStringLiteral("kcfg_ldappassword")); + mPassword->setEchoMode(QLineEdit::Password); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mPassword, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_HOST) { + label = new QLabel(i18n("Host:"), mParent); + mHost = new QLineEdit(mParent); + mHost->setObjectName(QStringLiteral("kcfg_ldaphost")); + mParent->connect(mHost, &QLineEdit::textChanged, mParent, &LdapConfigWidget::hostNameChanged); + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mHost, row, 1, 1, 3); + row++; + } + + col = 0; + if (mFeatures & W_PORT) { + label = new QLabel(i18n("Port:"), mParent); + mPort = new QSpinBox(mParent); + mPort->setRange(0, 65535); + mPort->setObjectName(QStringLiteral("kcfg_ldapport")); + mPort->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mPort->setValue(389); + + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mPort, row, col + 1); + col += 2; + } + + if (mFeatures & W_VER) { + label = new QLabel(i18n("LDAP version:"), mParent); + mVersion = new QSpinBox(mParent); + mVersion->setRange(2, 3); + mVersion->setObjectName(QStringLiteral("kcfg_ldapver")); + mVersion->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mVersion->setValue(3); + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mVersion, row, col + 1); + } + if (mFeatures & (W_PORT | W_VER)) { + row++; + } + + col = 0; + if (mFeatures & W_SIZELIMIT) { + label = new QLabel(i18n("Size limit:"), mParent); + mSizeLimit = new QSpinBox(mParent); + mSizeLimit->setRange(0, 9999999); + mSizeLimit->setObjectName(QStringLiteral("kcfg_ldapsizelimit")); + mSizeLimit->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mSizeLimit->setValue(0); + mSizeLimit->setSpecialValueText(i18nc("default ldap size limit", "Default")); + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mSizeLimit, row, col + 1); + col += 2; + } + + if (mFeatures & W_TIMELIMIT) { + label = new QLabel(i18n("Time limit:"), mParent); + mTimeLimit = new QSpinBox(mParent); + mTimeLimit->setRange(0, 9999999); + mTimeLimit->setObjectName(QStringLiteral("kcfg_ldaptimelimit")); + mTimeLimit->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mTimeLimit->setValue(0); + mTimeLimit->setSuffix(i18n(" sec")); + mTimeLimit->setSpecialValueText(i18nc("default ldap time limit", "Default")); + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mTimeLimit, row, col + 1); + } + if (mFeatures & (W_SIZELIMIT | W_TIMELIMIT)) { + row++; + } + + if (mFeatures & W_PAGESIZE) { + label = new QLabel(i18n("Page size:"), mParent); + mPageSize = new QSpinBox(mParent); + mPageSize->setRange(0, 9999999); + mPageSize->setObjectName(QStringLiteral("kcfg_ldappagesize")); + mPageSize->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mPageSize->setValue(0); + mPageSize->setSpecialValueText(i18n("No paging")); + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mPageSize, row++, 1); + } + + if (mFeatures & W_DN) { + label = new QLabel(i18nc("Distinguished Name", "DN:"), mParent); + mDn = new QLineEdit(mParent); + mDn->setObjectName(QStringLiteral("kcfg_ldapdn")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mDn, row, 1, 1, 1); + //without host query doesn't make sense + if (mHost) { + QPushButton *dnquery = new QPushButton(i18n("Query Server"), mParent); + connect(dnquery, SIGNAL(clicked()), mParent, SLOT(queryDNClicked())); + mainLayout->addWidget(dnquery, row, 2, 1, 1); + } + row++; + } + + if (mFeatures & W_FILTER) { + label = new QLabel(i18n("Filter:"), mParent); + mFilter = new QLineEdit(mParent); + mFilter->setObjectName(QStringLiteral("kcfg_ldapfilter")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mFilter, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_SECBOX) { + QGroupBox *btgroup = new QGroupBox(i18n("Security"), mParent); + QHBoxLayout *hbox = new QHBoxLayout; + btgroup->setLayout(hbox); + mSecNo = new QRadioButton(i18nc("@option:radio set no security", "No"), btgroup); + mSecNo->setObjectName(QStringLiteral("kcfg_ldapnosec")); + hbox->addWidget(mSecNo); + mSecTLS = new QRadioButton(i18nc("@option:radio use TLS security", "TLS"), btgroup); + mSecTLS->setObjectName(QStringLiteral("kcfg_ldaptls")); + hbox->addWidget(mSecTLS); + mSecSSL = new QRadioButton(i18nc("@option:radio use SSL security", "SSL"), btgroup); + mSecSSL->setObjectName(QStringLiteral("kcfg_ldapssl")); + hbox->addWidget(mSecSSL); + mainLayout->addWidget(btgroup, row, 0, 1, 4); + + connect(mSecNo, SIGNAL(clicked()), mParent, SLOT(setLDAPPort())); + connect(mSecTLS, SIGNAL(clicked()), mParent, SLOT(setLDAPPort())); + connect(mSecSSL, SIGNAL(clicked()), mParent, SLOT(setLDAPSPort())); + + mSecNo->setChecked(true); + row++; + } + + if (mFeatures & W_AUTHBOX) { + + QGroupBox *authbox = + new QGroupBox(i18n("Authentication"), mParent); + QVBoxLayout *vbox = new QVBoxLayout; + authbox->setLayout(vbox); + QHBoxLayout *hbox = new QHBoxLayout; + vbox->addLayout(hbox); + + mAnonymous = + new QRadioButton(i18nc("@option:radio anonymous authentication", "Anonymous"), authbox); + mAnonymous->setObjectName(QStringLiteral("kcfg_ldapanon")); + hbox->addWidget(mAnonymous); + mSimple = + new QRadioButton(i18nc("@option:radio simple authentication", "Simple"), authbox); + mSimple->setObjectName(QStringLiteral("kcfg_ldapsimple")); + hbox->addWidget(mSimple); + mSASL = + new QRadioButton(i18nc("@option:radio SASL authentication", "SASL"), authbox); + mSASL->setObjectName(QStringLiteral("kcfg_ldapsasl")); + hbox->addWidget(mSASL); + + hbox = new QHBoxLayout; + vbox->addLayout(hbox); + label = new QLabel(i18n("SASL mechanism:"), authbox); + hbox->addWidget(label); + mMech = new KComboBox(false, authbox); + mMech->setObjectName(QStringLiteral("kcfg_ldapsaslmech")); + mMech->setEditable(true); + mMech->addItem(QStringLiteral("DIGEST-MD5")); + mMech->addItem(QStringLiteral("GSSAPI")); + mMech->addItem(QStringLiteral("PLAIN")); + hbox->addWidget(mMech); + + //without host query doesn't make sense + if (mHost) { + mQueryMech = new QPushButton(i18n("Query Server"), authbox); + hbox->addWidget(mQueryMech); + connect(mQueryMech, SIGNAL(clicked()), mParent, SLOT(queryMechClicked())); + } + + mainLayout->addWidget(authbox, row, 0, 2, 4); + + connect(mAnonymous, SIGNAL(toggled(bool)), mParent, SLOT(setAnonymous(bool))); + connect(mSimple, SIGNAL(toggled(bool)), mParent, SLOT(setSimple(bool))); + connect(mSASL, SIGNAL(toggled(bool)), mParent, SLOT(setSASL(bool))); + + mAnonymous->setChecked(true); + } +} + +void LdapConfigWidget::Private::sendQuery() +{ + LdapServer _server(mParent->server()); + + mQResult.clear(); + mCancelled = true; + + if (mAttr == QLatin1String("supportedsaslmechanisms")) { + _server.setAuth(LdapServer::Anonymous); + } + + LdapUrl _url(_server.url()); + + _url.setDn(LdapDN(QStringLiteral(""))); + _url.setAttributes(QStringList(mAttr)); + _url.setScope(LdapUrl::Base); + + qCDebug(LDAP_LOG) << "sendQuery url:" << _url.toDisplayString(); + + LdapSearch search; + connect(&search, SIGNAL(data(KLDAP::LdapSearch*,KLDAP::LdapObject)), + mParent, SLOT(loadData(KLDAP::LdapSearch*,KLDAP::LdapObject))); + connect(&search, SIGNAL(result(KLDAP::LdapSearch*)), + mParent, SLOT(loadResult(KLDAP::LdapSearch*))); + + if (!search.search(_url)) { + KMessageBox::error(mParent, search.errorString()); + return; + } + + if (mProg == Q_NULLPTR) { + mProg = new QProgressDialog(mParent); + mProg->setWindowTitle(i18n("LDAP Query")); + mProg->setModal(true); + } + mProg->setLabelText(_url.toDisplayString()); + mProg->setMaximum(1); + mProg->setMinimum(0); + mProg->setValue(0); + mProg->exec(); + if (mCancelled) { + qCDebug(LDAP_LOG) << "query canceled!"; + search.abandon(); + } else { + if (search.error()) { + if (search.errorString().isEmpty()) { + KMessageBox::error(mParent, i18nc("%1 is a url to ldap server", "Unknown error connecting %1", _url.toDisplayString())); + } else { + KMessageBox::error(mParent, search.errorString()); + } + } + } +} + +void LdapConfigWidget::Private::queryMechClicked() +{ + mAttr = QStringLiteral("supportedsaslmechanisms"); + sendQuery(); + if (!mQResult.isEmpty()) { + mQResult.sort(); + mMech->clear(); + mMech->addItems(mQResult); + } +} + +void LdapConfigWidget::Private::queryDNClicked() +{ + mAttr = QStringLiteral("namingcontexts"); + sendQuery(); + if (!mQResult.isEmpty()) { + mDn->setText(mQResult.first()); + } +} + +void LdapConfigWidget::Private::loadData(LdapSearch *, const LdapObject &object) +{ + qCDebug(LDAP_LOG) << "object:" << object.toString(); + mProg->setValue(mProg->value() + 1); + LdapAttrMap::ConstIterator end(object.attributes().constEnd()); + for (LdapAttrMap::ConstIterator it = object.attributes().constBegin(); + it != end; ++it) { + LdapAttrValue::ConstIterator end2((*it).constEnd()); + for (LdapAttrValue::ConstIterator it2 = (*it).constBegin(); + it2 != end2; ++it2) { + mQResult.push_back(QString::fromUtf8(*it2)); + } + } +} + +void LdapConfigWidget::Private::loadResult(LdapSearch *search) +{ + Q_UNUSED(search); + mCancelled = false; + mProg->close(); +} + +void LdapConfigWidget::Private::setAnonymous(bool on) +{ + if (!on) { + return; + } + if (mUser) { + mUser->setEnabled(false); + } + if (mPassword) { + mPassword->setEnabled(false); + } + if (mBindDn) { + mBindDn->setEnabled(false); + } + if (mRealm) { + mRealm->setEnabled(false); + } + if (mMech) { + mMech->setEnabled(false); + } + if (mQueryMech) { + mQueryMech->setEnabled(false); + } +} + +void LdapConfigWidget::Private::setSimple(bool on) +{ + if (!on) { + return; + } + if (mUser) { + mUser->setEnabled(false); + } + if (mPassword) { + mPassword->setEnabled(true); + } + if (mBindDn) { + mBindDn->setEnabled(true); + } + if (mRealm) { + mRealm->setEnabled(false); + } + if (mMech) { + mMech->setEnabled(false); + } + if (mQueryMech) { + mQueryMech->setEnabled(false); + } +} + +void LdapConfigWidget::Private::setSASL(bool on) +{ + if (!on) { + return; + } + if (mUser) { + mUser->setEnabled(true); + } + if (mPassword) { + mPassword->setEnabled(true); + } + if (mBindDn) { + mBindDn->setEnabled(true); + } + if (mRealm) { + mRealm->setEnabled(true); + } + if (mMech) { + mMech->setEnabled(true); + } + if (mQueryMech) { + mQueryMech->setEnabled(true); + } +} + +void LdapConfigWidget::Private::setLDAPPort() +{ + if (mPort) { + mPort->setValue(389); + } +} + +void LdapConfigWidget::Private::setLDAPSPort() +{ + if (mPort) { + mPort->setValue(636); + } +} + +LdapConfigWidget::LdapConfigWidget(QWidget *parent, Qt::WindowFlags fl) + : QWidget(parent, fl), d(new Private(this)) +{ +} + +LdapConfigWidget::LdapConfigWidget(LdapConfigWidget::WinFlags flags, + QWidget *parent, Qt::WindowFlags fl) + : QWidget(parent, fl), d(new Private(this)) +{ + d->mFeatures = flags; + + d->initWidget(); +} + +LdapConfigWidget::~LdapConfigWidget() +{ + delete d; +} + +LdapUrl LdapConfigWidget::url() const +{ + return server().url(); +} + +void LdapConfigWidget::setUrl(const LdapUrl &url) +{ + LdapServer _server; + _server.setUrl(url); + setServer(_server); +} + +LdapServer LdapConfigWidget::server() const +{ + LdapServer _server; + if (d->mSecSSL && d->mSecSSL->isChecked()) { + _server.setSecurity(LdapServer::SSL); + } else if (d->mSecTLS && d->mSecTLS->isChecked()) { + _server.setSecurity(LdapServer::TLS); + } else { + _server.setSecurity(LdapServer::None); + } + + if (d->mUser) { + _server.setUser(d->mUser->text()); + } + if (d->mBindDn) { + _server.setBindDn(d->mBindDn->text()); + } + if (d->mPassword) { + _server.setPassword(d->mPassword->text()); + } + if (d->mRealm) { + _server.setRealm(d->mRealm->text()); + } + if (d->mHost) { + _server.setHost(d->mHost->text()); + } + if (d->mPort) { + _server.setPort(d->mPort->value()); + } + if (d->mDn) { + _server.setBaseDn(LdapDN(d->mDn->text())); + } + if (d->mFilter) { + _server.setFilter(d->mFilter->text()); + } + if (d->mVersion) { + _server.setVersion(d->mVersion->value()); + } + if (d->mSizeLimit && d->mSizeLimit->value() != 0) { + _server.setSizeLimit(d->mSizeLimit->value()); + } + if (d->mTimeLimit && d->mTimeLimit->value() != 0) { + _server.setTimeLimit(d->mTimeLimit->value()); + } + if (d->mPageSize && d->mPageSize->value() != 0) { + _server.setPageSize(d->mPageSize->value()); + } + if (d->mAnonymous && d->mAnonymous->isChecked()) { + _server.setAuth(LdapServer::Anonymous); + } else if (d->mSimple && d->mSimple->isChecked()) { + _server.setAuth(LdapServer::Simple); + } else if (d->mSASL && d->mSASL->isChecked()) { + _server.setAuth(LdapServer::SASL); + _server.setMech(d->mMech->currentText()); + } + return _server; +} + +void LdapConfigWidget::setServer(const LdapServer &server) +{ + switch (server.security()) { + case LdapServer::SSL: + if (d->mSecSSL) { + d->mSecSSL->setChecked(true); + } + break; + case LdapServer::TLS: + if (d->mSecTLS) { + d->mSecTLS->setChecked(true); + } + break; + case LdapServer::None: + if (d->mSecNo) { + d->mSecNo->setChecked(true); + } + break; + } + + switch (server.auth()) { + case LdapServer::Anonymous: + if (d->mAnonymous) { + d->mAnonymous->setChecked(true); + } + break; + case LdapServer::Simple: + if (d->mSimple) { + d->mSimple->setChecked(true); + } + break; + case LdapServer::SASL: + if (d->mSASL) { + d->mSASL->setChecked(true); + } + break; + } + + setUser(server.user()); + setBindDn(server.bindDn()); + setPassword(server.password()); + setRealm(server.realm()); + setHost(server.host()); + setPort(server.port()); + setFilter(server.filter()); + setDn(server.baseDn()); + setVersion(server.version()); + setSizeLimit(server.sizeLimit()); + setTimeLimit(server.timeLimit()); + setPageSize(server.pageSize()); + setMech(server.mech()); +} + +void LdapConfigWidget::setUser(const QString &user) +{ + if (d->mUser) { + d->mUser->setText(user); + } +} + +QString LdapConfigWidget::user() const +{ + return d->mUser ? d->mUser->text() : QString(); +} + +void LdapConfigWidget::setPassword(const QString &password) +{ + if (d->mPassword) { + d->mPassword->setText(password); + } +} + +QString LdapConfigWidget::password() const +{ + return d->mPassword ? d->mPassword->text() : QString(); +} + +void LdapConfigWidget::setBindDn(const QString &binddn) +{ + if (d->mBindDn) { + d->mBindDn->setText(binddn); + } +} + +QString LdapConfigWidget::bindDn() const +{ + return d->mBindDn ? d->mBindDn->text() : QString(); +} + +void LdapConfigWidget::setRealm(const QString &realm) +{ + if (d->mRealm) { + d->mRealm->setText(realm); + } +} + +QString LdapConfigWidget::realm() const +{ + return d->mRealm ? d->mRealm->text() : QString(); +} + +void LdapConfigWidget::setHost(const QString &host) +{ + if (d->mHost) { + d->mHost->setText(host); + } +} + +QString LdapConfigWidget::host() const +{ + return d->mHost ? d->mHost->text() : QString(); +} + +void LdapConfigWidget::setPort(int port) +{ + if (d->mPort) { + d->mPort->setValue(port); + } +} + +int LdapConfigWidget::port() const +{ + return d->mPort ? d->mPort->value() : 389; +} + +void LdapConfigWidget::setVersion(int version) +{ + if (d->mVersion) { + d->mVersion->setValue(version); + } +} + +int LdapConfigWidget::version() const +{ + return d->mVersion ? d->mVersion->value() : 3; +} + +void LdapConfigWidget::setDn(const LdapDN &dn) +{ + if (d->mDn) { + d->mDn->setText(dn.toString()); + } +} + +LdapDN LdapConfigWidget::dn() const +{ + return d->mDn ? LdapDN(d->mDn->text()) : LdapDN(); +} + +void LdapConfigWidget::setFilter(const QString &filter) +{ + if (d->mFilter) { + d->mFilter->setText(filter); + } +} + +QString LdapConfigWidget::filter() const +{ + return d->mFilter ? d->mFilter->text() : QString(); +} + +void LdapConfigWidget::setMech(const QString &mech) +{ + if (d->mMech == Q_NULLPTR) { + return; + } + if (!mech.isEmpty()) { + int i = 0; + while (i < d->mMech->count()) { + if (d->mMech->itemText(i) == mech) { + break; + } + i++; + } + if (i == d->mMech->count()) { + d->mMech->addItem(mech); + } + d->mMech->setCurrentIndex(i); + } +} + +QString LdapConfigWidget::mech() const +{ + return d->mMech ? d->mMech->currentText() : QString(); +} + +void LdapConfigWidget::setSecurity(Security security) +{ + switch (security) { + case None: + d->mSecNo->setChecked(true); + break; + case SSL: + d->mSecSSL->setChecked(true); + break; + case TLS: + d->mSecTLS->setChecked(true); + break; + } +} + +LdapConfigWidget::Security LdapConfigWidget::security() const +{ + if (d->mSecTLS->isChecked()) { + return TLS; + } + if (d->mSecSSL->isChecked()) { + return SSL; + } + return None; +} + +void LdapConfigWidget::setAuth(Auth auth) +{ + switch (auth) { + case Anonymous: + d->mAnonymous->setChecked(true); + break; + case Simple: + d->mSimple->setChecked(true); + break; + case SASL: + d->mSASL->setChecked(true); + break; + } +} + +LdapConfigWidget::Auth LdapConfigWidget::auth() const +{ + if (d->mSimple->isChecked()) { + return Simple; + } + if (d->mSASL->isChecked()) { + return SASL; + } + return Anonymous; +} + +void LdapConfigWidget::setSizeLimit(int sizelimit) +{ + if (d->mSizeLimit) { + d->mSizeLimit->setValue(sizelimit); + } +} + +int LdapConfigWidget::sizeLimit() const +{ + return d->mSizeLimit ? d->mSizeLimit->value() : 0; +} + +void LdapConfigWidget::setTimeLimit(int timelimit) +{ + if (d->mTimeLimit) { + d->mTimeLimit->setValue(timelimit); + } +} + +int LdapConfigWidget::timeLimit() const +{ + return d->mTimeLimit ? d->mTimeLimit->value() : 0; +} + +void LdapConfigWidget::setPageSize(int pagesize) +{ + if (d->mPageSize) { + d->mPageSize->setValue(pagesize); + } +} + +int LdapConfigWidget::pageSize() const +{ + return d->mPageSize ? d->mPageSize->value() : 0; +} + +LdapConfigWidget::WinFlags LdapConfigWidget::features() const +{ + return d->mFeatures; +} + +void LdapConfigWidget::setFeatures(LdapConfigWidget::WinFlags features) +{ + d->mFeatures = features; + + // First delete all the child widgets. + // FIXME: I hope it's correct + QList ch = children(); + const int numberOfChild(ch.count()); + for (int i = 0; i < numberOfChild; ++i) { + QWidget *widget = dynamic_cast(ch[ i ]); + if (widget && widget->parent() == this) { + delete(widget); + } + } + + // Re-create child widgets according to the new flags + d->initWidget(); +} + +#include "moc_ldapconfigwidget.cpp" diff --git a/src/ldapconfigwidget.h b/src/ldapconfigwidget.h new file mode 100644 index 0000000..19ce35f --- /dev/null +++ b/src/ldapconfigwidget.h @@ -0,0 +1,292 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPCONFIGWIDGET_H +#define KLDAP_LDAPCONFIGWIDGET_H + +#include +#include + +#include "ldapdn.h" +#include "kldap_export.h" +#include "ldapobject.h" +#include "ldapserver.h" +#include "ldapurl.h" + +namespace KLDAP +{ + +class LdapSearch; + +/** + @brief LDAP Configuration widget + + This class can be used to query the user for LDAP connection parameters. + It's KConfigXT compatible, using widget names starting with kcfg_ +*/ + +class KLDAP_EXPORT LdapConfigWidget : public QWidget +{ + Q_OBJECT + Q_FLAGS(WinFlags) + Q_ENUMS(Security) + Q_ENUMS(Auth) + Q_PROPERTY(WinFlags features READ features WRITE setFeatures) + Q_PROPERTY(QString user READ user WRITE setUser) + Q_PROPERTY(QString bindDn READ bindDn WRITE setBindDn) + Q_PROPERTY(QString realm READ realm WRITE setRealm) + Q_PROPERTY(QString password READ password WRITE setPassword) + Q_PROPERTY(QString host READ host WRITE setHost) + Q_PROPERTY(int port READ port WRITE setPort) + Q_PROPERTY(int version READ version WRITE setVersion) + Q_PROPERTY(LdapDN dn READ dn WRITE setDn) + Q_PROPERTY(QString filter READ filter WRITE setFilter) + Q_PROPERTY(QString mech READ mech WRITE setMech) + Q_PROPERTY(Security security READ security WRITE setSecurity) + Q_PROPERTY(Auth auth READ auth WRITE setAuth) + Q_PROPERTY(int sizeLimit READ sizeLimit WRITE setSizeLimit) + Q_PROPERTY(int timeLimit READ timeLimit WRITE setTimeLimit) + Q_PROPERTY(int pageSize READ pageSize WRITE setPageSize) + +public: + + enum WinFlag { + W_USER = 0x1, + W_BINDDN = 0x2, + W_REALM = 0x4, + W_PASS = 0x8, + W_HOST = 0x10, + W_PORT = 0x20, + W_VER = 0x40, + W_DN = 0x80, + W_FILTER = 0x100, + W_SECBOX = 0x200, + W_AUTHBOX = 0x400, + W_TIMELIMIT = 0x800, + W_SIZELIMIT = 0x1000, + W_PAGESIZE = 0x2000, + W_ALL = 0x2fff + }; + + typedef enum { + None, SSL, TLS + } Security; + typedef enum { + Anonymous, Simple, SASL + } Auth; + + Q_DECLARE_FLAGS(WinFlags, WinFlag) + + /** Constructs an empty configuration widget. + * You need to call setFlags() after this. + * @param parent the QWidget parent + * @param fl the window flags to set + */ + explicit LdapConfigWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags fl = 0); + /** Constructs a configuration widget */ + explicit LdapConfigWidget(WinFlags flags, QWidget *parent = Q_NULLPTR, + Qt::WindowFlags fl = 0); + /** Destructs a configuration widget */ + virtual ~LdapConfigWidget(); + + /** Sets the user name. Kconfig widget name: kcfg_ldapuser + * @param user the user name to set + */ + void setUser(const QString &user); + /** Gets the user name. Kconfig widget name: kcfg_ldapuser */ + QString user() const; + + /** Sets the password. Kconfig widget name: kcfg_ldappassword + * @param password the password to set + */ + void setPassword(const QString &password); + /** Gets the password. Kconfig widget name: kcfg_ldappassword */ + QString password() const; + + /** + * Sets the bind dn. + * Kconfig widget name: kcfg_ldapbinddn + * @param binddn the LDAP Bind DN to set + */ + void setBindDn(const QString &binddn); + /** Gets the bind dn. Kconfig widget name: kcfg_ldapbinddn*/ + QString bindDn() const; + + /** Sets the SASL realm. Kconfig widget name: kcfg_ldaprealm + * @param realm the SASL realm to set + */ + void setRealm(const QString &realm); + /** Gets the SASL realm. Kconfig widget name: kcfg_ldaprealm */ + QString realm() const; + + /** Sets the host name. Kconfig widget name: kcfg_ldaphost + * @param host the LDAP host to set + */ + void setHost(const QString &host); + /** Gets the host name. Kconfig widget name: kcfg_ldaphost */ + QString host() const; + + /** Sets the LDAP port. Kconfig widget name: kcfg_ldapport + * @param port the LDAP port to set + */ + void setPort(int port); + /** Gets the LDAP port. Kconfig widget name: kcfg_ldapport */ + int port() const; + + /** Sets the LDAP protocol version. Kconfig widget name: kcfg_ldapver + * @param version the LDAP protocol version to set + */ + void setVersion(int version); + /** Gets the LDAP protocol version. Kconfig widget name: kcfg_ldapver */ + int version() const; + + /** Sets the LDAP Base DN. Kconfig widget name: kcfg_ldapdn + * @param dn the LDAP Base DN to set + */ + void setDn(const LdapDN &dn); + /** Gets the LDAP Base DN. Kconfig widget name: kcfg_ldapdn */ + LdapDN dn() const; + + /** Sets the LDAP Filter. Kconfig widget name: kcfg_ldapfilter + * @param filter the LDAP Filter to set + */ + void setFilter(const QString &filter); + /** Gets the LDAP Filter. Kconfig widget name: kcfg_ldapfilter */ + QString filter() const; + + /** Sets the SASL Mechanism. Kconfig widget name: kcfg_ldapsaslmech + * @param mech the SASL Mechanism to set + */ + void setMech(const QString &mech); + /** Gets the SASL Mechanism. Kconfig widget name: kcfg_ldapsaslmech */ + QString mech() const; + + /** + * Sets the security type (None, SSL, TLS). + * Kconfig widget names: kcfg_ldapnosec, kcfg_ldaptls, kcfg_ldapssl + * @param security the security type to set + */ + void setSecurity(Security security); + /** + * Returns the security type. + * Kconfig widget names: kcfg_ldapnosec, kcfg_ldaptls, kcfg_ldapssl + * @param security the security type to set + */ + Security security() const; + + /** + * Sets the authentication type (Anonymous, Simple, SASL). + * Kconfig widget names: kcfg_ldapanon, kcfg_ldapsimple, kcfg_ldapsasl + * @param auth the authentication type to set + */ + void setAuth(Auth auth); + /** + * Returns the authentication type. + * Kconfig widget names: kcfg_ldapanon, kcfg_ldapsimple, kcfg_ldapsasl + * @param auth the authentication type to set + */ + Auth auth() const; + + /** + * Sets the size limit. + * KConfig widget name: kcfg_ldapsizelimit + * @param sizelimit the size limit to set + */ + void setSizeLimit(int sizelimit); + /** + * Returns the size limit. + * KConfig widget name: kcfg_ldapsizelimit + */ + int sizeLimit() const; + + /** + * Sets the time limit. + * KConfig widget name: kcfg_ldaptimelimit + * @param timelimit the time limit to set + */ + void setTimeLimit(int timelimit); + /** + * Returns the time limit. + * KConfig widget name: kcfg_ldaptimelimit + */ + int timeLimit() const; + + /** + * Sets the page size. + * KConfig widget name: kcfg_ldappagesize + * @param pagesize the page size to set + */ + void setPageSize(int pagesize); + /** + * Returns the page size. + * KConfig widget name: kcfg_ldappagesize + */ + int pageSize() const; + + WinFlags features() const; + void setFeatures(WinFlags features); + + /** + * Returns a LDAP Url constructed from the settings given. + * Extensions are filled for use in the LDAP ioslave + */ + LdapUrl url() const; + /** + * Set up the widget via an LDAP Url. + * @param url the LDAP Url to set + */ + void setUrl(const LdapUrl &url); + + /** + * Returns an LdapServer object constructed from the settings given. + */ + LdapServer server() const; + /** + * Set up the widget via an LdapServer object. + * @param server the LdapServer object to set + */ + void setServer(const LdapServer &server); + +Q_SIGNALS: + /** + * @since 4.13 + */ + void hostNameChanged(const QString &); + +private: + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void setLDAPPort()) + Q_PRIVATE_SLOT(d, void setLDAPSPort()) + Q_PRIVATE_SLOT(d, void setAnonymous(bool)) + Q_PRIVATE_SLOT(d, void setSimple(bool)) + Q_PRIVATE_SLOT(d, void setSASL(bool)) + Q_PRIVATE_SLOT(d, void queryDNClicked()) + Q_PRIVATE_SLOT(d, void queryMechClicked()) + Q_PRIVATE_SLOT(d, void loadData(KLDAP::LdapSearch *, const KLDAP::LdapObject &)) + Q_PRIVATE_SLOT(d, void loadResult(KLDAP::LdapSearch *)) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(LdapConfigWidget::WinFlags) + +} + +#endif diff --git a/src/ldapconnection.cpp b/src/ldapconnection.cpp new file mode 100644 index 0000000..86de3d1 --- /dev/null +++ b/src/ldapconnection.cpp @@ -0,0 +1,430 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapconnection.h" +#include "ldapdefs.h" +#include "kldap_config.h" // SASL2_FOUND, LDAP_FOUND + +#include +#include +#include "ldap_debug.h" + +#ifdef SASL2_FOUND +#include +static const sasl_callback_t callbacks[] = { + { SASL_CB_ECHOPROMPT, Q_NULLPTR, Q_NULLPTR }, + { SASL_CB_NOECHOPROMPT, Q_NULLPTR, Q_NULLPTR }, + { SASL_CB_GETREALM, Q_NULLPTR, Q_NULLPTR }, + { SASL_CB_USER, Q_NULLPTR, Q_NULLPTR }, + { SASL_CB_AUTHNAME, Q_NULLPTR, Q_NULLPTR }, + { SASL_CB_PASS, Q_NULLPTR, Q_NULLPTR }, + { SASL_CB_CANON_USER, Q_NULLPTR, Q_NULLPTR }, + { SASL_CB_LIST_END, Q_NULLPTR, Q_NULLPTR } +}; + +static bool ldapoperation_sasl_initialized = false; +#endif + +#ifdef LDAP_FOUND +# ifndef HAVE_WINLDAP_H +# include +# include +#else +# include +#endif // HAVE_WINLDAP_H + +#ifndef LDAP_OPT_SUCCESS +#define LDAP_OPT_SUCCESS 0 +#endif + +#endif + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapConnection::LdapConnectionPrivate +{ +public: + LdapConnectionPrivate(); + LdapServer mServer; + QString mConnectionError; + +#ifdef LDAP_FOUND + LDAP *mLDAP; +#else + void *mLDAP; +#endif +#ifdef SASL2_FOUND + sasl_conn_t *mSASLconn; +#else + void *mSASLconn; +#endif + +}; + +LdapConnection::LdapConnectionPrivate::LdapConnectionPrivate() +{ + mSASLconn = Q_NULLPTR; +#ifdef SASL2_FOUND + if (!ldapoperation_sasl_initialized) { + sasl_client_init(Q_NULLPTR); + ldapoperation_sasl_initialized = true; + } +#endif +} + +LdapConnection::LdapConnection() + : d(new LdapConnectionPrivate) +{ + d->mLDAP = Q_NULLPTR; +} + +LdapConnection::LdapConnection(const LdapUrl &url) + : d(new LdapConnectionPrivate) +{ + d->mLDAP = Q_NULLPTR; + setUrl(url); +} + +LdapConnection::LdapConnection(const LdapServer &server) + : d(new LdapConnectionPrivate) +{ + d->mLDAP = Q_NULLPTR; + setServer(server); +} + +LdapConnection::~LdapConnection() +{ + close(); + delete d; +} + +void LdapConnection::setUrl(const LdapUrl &url) +{ + d->mServer.setUrl(url); +} + +void LdapConnection::setServer(const LdapServer &server) +{ + d->mServer = server; +} + +const LdapServer &LdapConnection::server() const +{ + return d->mServer; +} + +void *LdapConnection::handle() const +{ + return (void *)d->mLDAP; +} + +void *LdapConnection::saslHandle() const +{ + return (void *)d->mSASLconn; +} + +QString LdapConnection::errorString(int code) +{ + //No translated error messages yet +#ifdef LDAP_FOUND + return QString::fromUtf8(ldap_err2string(code)); +#else + return i18n("No LDAP Support..."); +#endif +} + +QString LdapConnection::saslErrorString() const +{ +#ifdef SASL2_FOUND + const char *str; + str = sasl_errdetail(d->mSASLconn); + return QString::fromLocal8Bit(str); +#else + return i18n("SASL support is not available. Please recompile libkldap with the " + "Cyrus-SASL (or compatible) client libraries, or complain to your " + "distribution packagers."); +#endif +} + +QString LdapConnection::connectionError() const +{ + return d->mConnectionError; +} + +#ifdef LDAP_FOUND +int LdapConnection::getOption(int option, void *value) const +{ + Q_ASSERT(d->mLDAP); + return ldap_get_option(d->mLDAP, option, value); +} + +int LdapConnection::setOption(int option, void *value) +{ + Q_ASSERT(d->mLDAP); + return ldap_set_option(d->mLDAP, option, value); +} + +int LdapConnection::ldapErrorCode() const +{ + Q_ASSERT(d->mLDAP); + int err; + ldap_get_option(d->mLDAP, LDAP_OPT_ERROR_NUMBER, &err); + return err; +} + +QString LdapConnection::ldapErrorString() const +{ + Q_ASSERT(d->mLDAP); + char *errmsg; + ldap_get_option(d->mLDAP, LDAP_OPT_ERROR_STRING, &errmsg); + QString msg = QString::fromLocal8Bit(errmsg); + free(errmsg); + return msg; +} + +bool LdapConnection::setSizeLimit(int sizelimit) +{ + Q_ASSERT(d->mLDAP); + qCDebug(LDAP_LOG) << "sizelimit:" << sizelimit; + if (setOption(LDAP_OPT_SIZELIMIT, &sizelimit) != LDAP_OPT_SUCCESS) { + return false; + } + return true; +} + +int LdapConnection::sizeLimit() const +{ + Q_ASSERT(d->mLDAP); + int sizelimit; + if (getOption(LDAP_OPT_SIZELIMIT, &sizelimit) != LDAP_OPT_SUCCESS) { + return -1; + } + return sizelimit; +} + +bool LdapConnection::setTimeLimit(int timelimit) +{ + Q_ASSERT(d->mLDAP); + qCDebug(LDAP_LOG) << "timelimit:" << timelimit; + if (setOption(LDAP_OPT_TIMELIMIT, &timelimit) != LDAP_OPT_SUCCESS) { + return false; + } + return true; +} + +int LdapConnection::timeLimit() const +{ + Q_ASSERT(d->mLDAP); + int timelimit; + if (getOption(LDAP_OPT_TIMELIMIT, &timelimit) != LDAP_OPT_SUCCESS) { + return -1; + } + return timelimit; +} + +int LdapConnection::connect() +{ + int ret; + QString url; + if (d->mLDAP) { + close(); + } + + int version = d->mServer.version(); + int timeout = d->mServer.timeout(); + + url = d->mServer.security() == LdapServer::SSL ? QStringLiteral("ldaps") : QStringLiteral("ldap"); + url += QLatin1String("://"); + url += d->mServer.host(); + url += QLatin1Char(':'); + url += QString::number(d->mServer.port()); + qCDebug(LDAP_LOG) << "ldap url:" << url; +#ifdef HAVE_LDAP_INITIALIZE + ret = ldap_initialize(&d->mLDAP, url.toLatin1().constData()); +#else + d->mLDAP = ldap_init(d->mServer.host().toLatin1().data(), d->mServer.port()); + if (d->mLDAP == 0) { + ret = -1; + } else { + ret = LDAP_SUCCESS; + } +#endif + if (ret != LDAP_SUCCESS) { + d->mConnectionError = i18n("An error occurred during the connection initialization phase."); + return ret; + } + + qCDebug(LDAP_LOG) << "setting version to:" << version; + if (setOption(LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) { + ret = ldapErrorCode(); + d->mConnectionError = i18n("Cannot set protocol version to %1.", version); + close(); + return ret; + } + +#if defined(LDAP_OPT_TIMEOUT) + qCDebug(LDAP_LOG) << "setting timeout to:" << timeout; + + if (timeout) { + if (setOption(LDAP_OPT_TIMEOUT, &timeout) != LDAP_OPT_SUCCESS) { + ret = ldapErrorCode(); + d->mConnectionError = i18np("Cannot set timeout to %1 second.", + "Cannot set timeout to %1 seconds.", + timeout); + close(); + return ret; + } + } +#endif + + //FIXME: accessing to certificate handling would be good + qCDebug(LDAP_LOG) << "setting security to:" << d->mServer.security(); + if (d->mServer.security() == LdapServer::TLS) { + qCDebug(LDAP_LOG) << "start TLS"; +#ifdef HAVE_LDAP_START_TLS_S + if ((ret = ldap_start_tls_s(d->mLDAP, Q_NULLPTR, Q_NULLPTR)) != LDAP_SUCCESS) { + d->mConnectionError = ldapErrorString(); + close(); + return ret; + } +#else + close(); + d->mConnectionError = i18n("TLS support not available in the LDAP client libraries."); + return -1; +#endif + } + + qCDebug(LDAP_LOG) << "setting sizelimit to:" << d->mServer.sizeLimit(); + if (d->mServer.sizeLimit()) { + if (!setSizeLimit(d->mServer.sizeLimit())) { + ret = ldapErrorCode(); + close(); + d->mConnectionError = i18n("Cannot set size limit."); + return ret; + } + } + + qCDebug(LDAP_LOG) << "setting timelimit to:" << d->mServer.timeLimit(); + if (d->mServer.timeLimit()) { + if (!setTimeLimit(d->mServer.timeLimit())) { + ret = ldapErrorCode(); + close(); + d->mConnectionError = i18n("Cannot set time limit."); + return ret; + } + } + +#ifdef SASL2_FOUND + qCDebug(LDAP_LOG) << "initializing SASL client"; + int saslresult = sasl_client_new("ldap", d->mServer.host().toLatin1(), + Q_NULLPTR, Q_NULLPTR, callbacks, 0, &d->mSASLconn); + if (saslresult != SASL_OK) { + d->mConnectionError = i18n("Cannot initialize the SASL client."); + return KLDAP_SASL_ERROR; + } +#endif + + return 0; +} + +void LdapConnection::close() +{ + if (d->mLDAP) { +#ifdef HAVE_LDAP_UNBIND_EXT + ldap_unbind_ext(d->mLDAP, Q_NULLPTR, Q_NULLPTR); +#else + ldap_unbind(d->mLDAP); +#endif + } + d->mLDAP = Q_NULLPTR; +#ifdef SASL2_FOUND + if (d->mSASLconn) { + sasl_dispose(&d->mSASLconn); + d->mSASLconn = Q_NULLPTR; + } +#endif + qCDebug(LDAP_LOG) << "connection closed!"; +} +#else //LDAP_FOUND + +int LdapConnection::getOption(int option, void *value) const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +int LdapConnection::setOption(int option, void *value) +{ + qCritical() << "No LDAP support..."; + return -1; +} + +int LdapConnection::ldapErrorCode() const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +QString LdapConnection::ldapErrorString() const +{ + qCritical() << "No LDAP support..."; + return QString(); +} + +bool LdapConnection::setSizeLimit(int sizelimit) +{ + qCritical() << "No LDAP support..."; + return false; +} + +int LdapConnection::sizeLimit() const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +bool LdapConnection::setTimeLimit(int timelimit) +{ + qCritical() << "No LDAP support..."; + return false; +} + +int LdapConnection::timeLimit() const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +int LdapConnection::connect() +{ + d->mConnectionError = + i18n("LDAP support not compiled in. Please recompile libkldap with the " + "OpenLDAP (or compatible) client libraries, or complain to your " + "distribution packagers."); + qCritical() << "No LDAP support..."; + return -1; +} + +void LdapConnection::close() +{ + qCritical() << "No LDAP support..."; +} + +#endif diff --git a/src/ldapconnection.h b/src/ldapconnection.h new file mode 100644 index 0000000..5562ccf --- /dev/null +++ b/src/ldapconnection.h @@ -0,0 +1,146 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPCONNECTION_H +#define KLDAP_LDAPCONNECTION_H + +#include + +#include "ldapurl.h" +#include "ldapserver.h" +#include "kldap_export.h" + +namespace KLDAP +{ + +/** + * @brief + * This class represents a connection to an LDAP server. + */ +class KLDAP_EXPORT LdapConnection +{ +public: + + enum SASL_Fields { + SASL_Authname = 0x1, + SASL_Authzid = 0x2, + SASL_Realm = 0x4, + SASL_Password = 0x8 + }; + + /** Constructs an LdapConnection object */ + LdapConnection(); + /** Constructs an LdapConnection with the parameters given in url */ + explicit LdapConnection(const LdapUrl &url); + /** Constructs an LdapConnection with the parameters given in server */ + explicit LdapConnection(const LdapServer &server); + + virtual ~LdapConnection(); + + /** + * Sets the connection parameters via the specified url. After this, + * you need to call connect() to connect with the new parameters. + * @param url the URL containing the connection parameters + */ + void setUrl(const LdapUrl &url); + /** + * Returns the connection parameters which was specified with an LDAP Url + * or a LdapServer structure. + */ + const LdapServer &server() const; + /** + * Sets the connection parameters via the specified server structure. After + * this, you need to call connect() to connect with the new parameters. + * @param server the server object containing the connection parameters + */ + void setServer(const LdapServer &server); + + /** + * Sets up the connection parameters with creating a handle to the LDAP server. + * Also sets sizelimit and timelimit and starts TLS if it is requested. + * Returns 0 if successful, else returns an LDAP error code, and an error + * string which is available via connectionError(). + */ + int connect(); + /** + * Returns a translated error string if connect() failed. + */ + QString connectionError() const; + /** + * Closes the LDAP connection. + */ + void close(); + + /** Sets the size limit for the connection. + * @param sizelimit the connection size limit to set + */ + bool setSizeLimit(int sizelimit); + /** Returns the current size limit. */ + int sizeLimit() const; + + /** Sets the time limit for the connection. + * @param timelimit the connection time limit to set + */ + bool setTimeLimit(int timelimit); + /** Returns the current time limit. */ + int timeLimit() const; + + /** Gets an option from the connection. The option value can be client + * library specific, so avoid this function if possible + * @param option the connection option to return + * @param value the value of option to get + */ + int getOption(int option, void *value) const; + /** Sets an option in the connection. The option value can be client + * library specific, so avoid this function if possible */ + int setOption(int option, void *value); + + /** Returns the LDAP error code from the last operation */ + int ldapErrorCode() const; + /** Returns the LDAP error string from the last operation */ + QString ldapErrorString() const; + /** Returns a translated error message from the specified LDAP error code */ + static QString errorString(int code); + + /** Returns the SASL error string from the last SASL operation */ + QString saslErrorString() const; + + /** + * Returns the opaqe client-library specific LDAP object. + * Avoid its usage if you can. + */ + void *handle() const; + + /** + * Returns the opaqe sasl-library specific SASL object. + * Avoid its usage if you can. + */ + void *saslHandle() const; + +private: + class LdapConnectionPrivate; + LdapConnectionPrivate *const d; + + Q_DISABLE_COPY(LdapConnection) +}; + +} + +#endif diff --git a/src/ldapcontrol.cpp b/src/ldapcontrol.cpp new file mode 100644 index 0000000..4af0f2b --- /dev/null +++ b/src/ldapcontrol.cpp @@ -0,0 +1,158 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapcontrol.h" +#include "ber.h" + +#include + +using namespace KLDAP; + +class LdapControlPrivate : public QSharedData +{ +public: + LdapControlPrivate() + { + mCritical = false; + } + + LdapControlPrivate(const LdapControlPrivate &other) + : QSharedData(other) + { + mOid = other.mOid; + mValue = other.mValue; + mCritical = other.mCritical; + } + + QString mOid; + QByteArray mValue; + bool mCritical; +}; + +LdapControl::LdapControl() + : d(new LdapControlPrivate) +{ + setControl(QString(), QByteArray(), false); +} + +LdapControl::LdapControl(const QString &oid, const QByteArray &value, bool critical) + : d(new LdapControlPrivate) +{ + setControl(oid, value, critical); +} + +LdapControl::LdapControl(const LdapControl &that) + : d(that.d) +{ + setControl(that.d->mOid, that.d->mValue, that.d->mCritical); +} + +LdapControl &LdapControl::operator= (const LdapControl &that) +{ + if (this != &that) { + d = that.d; + } + + setControl(that.d->mOid, that.d->mValue, that.d->mCritical); + + return *this; +} + +LdapControl::~LdapControl() +{ +} + +void LdapControl::setControl(const QString &oid, const QByteArray &value, bool critical) +{ + d->mOid = oid; + d->mValue = value; + d->mCritical = critical; +} + +QString LdapControl::oid() const +{ + return d->mOid; +} + +QByteArray LdapControl::value() const +{ + return d->mValue; +} + +bool LdapControl::critical() const +{ + return d->mCritical; +} + +void LdapControl::setOid(const QString &oid) +{ + d->mOid = oid; +} + +void LdapControl::setValue(const QByteArray &value) +{ + d->mValue = value; +} + +void LdapControl::setCritical(bool critical) +{ + d->mCritical = critical; +} + +int LdapControl::parsePageControl(QByteArray &cookie) const +{ + if (d->mOid != QLatin1String("1.2.840.113556.1.4.319")) { + return -1; + } + + Ber ber(d->mValue); + int size; + if (ber.scanf(QStringLiteral("{iO}"), &size, &cookie) == -1) { + return -1; + } else { + return size; + } +} + +LdapControl LdapControl::createPageControl(int pagesize, const QByteArray &cookie) +{ + LdapControl control; + Ber ber; + + ber.printf(QStringLiteral("{iO}"), pagesize, &cookie); + control.setOid(QStringLiteral("1.2.840.113556.1.4.319")); + control.setValue(ber.flatten()); + return control; +} + +void LdapControl::insert(LdapControls &list, const LdapControl &ctrl) +{ + LdapControls::iterator it; + LdapControls::iterator endit = list.end(); + const QString oid = ctrl.oid(); + + for (it = list.begin(); it != endit; ++it) { + if (it->oid() == oid) { + *it = ctrl; + return; + } + } + list.append(ctrl); +} diff --git a/src/ldapcontrol.h b/src/ldapcontrol.h new file mode 100644 index 0000000..01a34eb --- /dev/null +++ b/src/ldapcontrol.h @@ -0,0 +1,118 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPCONTROL_H +#define KLDAP_LDAPCONTROL_H + +#include +#include +#include +class LdapControlPrivate; + +#include "kldap_export.h" + +namespace KLDAP +{ + +class LdapControl; +typedef QVector LdapControls; + +/** + @brief + This class represents an LDAP Control +*/ +class KLDAP_EXPORT LdapControl +{ +public: + /** + * Creates an empty control. + */ + LdapControl(); + /** + * Creates a control with the given OID, value and criticality. + */ + LdapControl(const QString &oid, const QByteArray &value, bool critical = false); + + LdapControl(const LdapControl &that); + LdapControl &operator= (const LdapControl &that); + /** + * Destroys the control object. + */ + virtual ~LdapControl(); + /** + * Sets the control's OID, value and criticality. + */ + void setControl(const QString &oid, const QByteArray &value, + bool critical = false); + /** + * Sets the control's OID. + */ + void setOid(const QString &oid); + /** + * Sets the control's value. + */ + void setValue(const QByteArray &value); + /** + * Sets the control's criticality. + */ + void setCritical(bool critical); + /** + * Returns the control's OID. + */ + QString oid() const; + /** + * Returns the control's value. + */ + QByteArray value() const; + /** + * Returns the control's criticality. + */ + bool critical() const; + + /** + * Parses a paging results control, which the server returned. + * Puts the server's cookie into @p cookie, and returns the estimated + * result set size. If the OID is not the page control's OID, or the + * value cannot be decoded, returns -1. + * @param cookie the cookie to hold server's cookie + */ + int parsePageControl(QByteArray &cookie) const; + /** + * Creates a paging search control. + */ + static LdapControl createPageControl(int pagesize, const QByteArray &cookie = QByteArray()); + + /** + * Inserts a unique control against a list of controls. + * If the control already exists in the list is is updated, otherwise + * it is appended to the list. + * @param list the current list of controls + * @param ctrl the control to insert + * @since 4.4 + */ + static void insert(LdapControls &list, const LdapControl &ctrl); + +private: + QSharedDataPointer d; +}; + +} + +#endif diff --git a/src/ldapdefs.h b/src/ldapdefs.h new file mode 100644 index 0000000..9a643f9 --- /dev/null +++ b/src/ldapdefs.h @@ -0,0 +1,161 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_DEFS_H +#define KLDAP_DEFS_H + +/** + * LDAP Error codes. + * These codes taken from openldap's ldap.h, and prefixed with KLDAP_ + * instead of LDAP_, just for applications which uses the kldap library + * doesn't need to include openldap headers + */ + +#define KLDAP_SUCCESS 0x00 + +#define KLDAP_RANGE(n,x,y) (((x) <= (n)) && ((n) <= (y))) + +#define KLDAP_OPERATIONS_ERROR 0x01 +#define KLDAP_PROTOCOL_ERROR 0x02 +#define KLDAP_TIMELIMIT_EXCEEDED 0x03 +#define KLDAP_SIZELIMIT_EXCEEDED 0x04 +#define KLDAP_COMPARE_FALSE 0x05 +#define KLDAP_COMPARE_TRUE 0x06 +#define KLDAP_AUTH_METHOD_NOT_SUPPORTED 0x07 +#define KLDAP_STRONG_AUTH_NOT_SUPPORTED KLDAP_AUTH_METHOD_NOT_SUPPORTED +#define KLDAP_STRONG_AUTH_REQUIRED 0x08 +#define KLDAP_STRONGER_AUTH_REQUIRED KLDAP_STRONG_AUTH_REQUIRED +#define KLDAP_PARTIAL_RESULTS 0x09 /* LDAPv2+ (not LDAPv3) */ + +#define KLDAP_REFERRAL 0x0a /* LDAPv3 */ +#define KLDAP_ADMINLIMIT_EXCEEDED 0x0b /* LDAPv3 */ +#define KLDAP_UNAVAILABLE_CRITICAL_EXTENSION 0x0c /* LDAPv3 */ +#define KLDAP_CONFIDENTIALITY_REQUIRED 0x0d /* LDAPv3 */ +#define KLDAP_SASL_BIND_IN_PROGRESS 0x0e /* LDAPv3 */ + +#define KLDAP_ATTR_ERROR(n) KLDAP_RANGE((n),0x10,0x15) /* 16-21 */ + +#define KLDAP_NO_SUCH_ATTRIBUTE 0x10 +#define KLDAP_UNDEFINED_TYPE 0x11 +#define KLDAP_INAPPROPRIATE_MATCHING 0x12 +#define KLDAP_CONSTRAINT_VIOLATION 0x13 +#define KLDAP_TYPE_OR_VALUE_EXISTS 0x14 +#define KLDAP_INVALID_SYNTAX 0x15 + +#define KLDAP_NAME_ERROR(n) KLDAP_RANGE((n),0x20,0x24) /* 32-34,36 */ + +#define KLDAP_NO_SUCH_OBJECT 0x20 +#define KLDAP_ALIAS_PROBLEM 0x21 +#define KLDAP_INVALID_DN_SYNTAX 0x22 +#define KLDAP_IS_LEAF 0x23 /* not LDAPv3 */ +#define KLDAP_ALIAS_DEREF_PROBLEM 0x24 + +#define KLDAP_SECURITY_ERROR(n) KLDAP_RANGE((n),0x2F,0x32) /* 47-50 */ + +#define KLDAP_PROXY_AUTHZ_FAILURE 0x2F /* LDAPv3 proxy authorization */ +#define KLDAP_INAPPROPRIATE_AUTH 0x30 +#define KLDAP_INVALID_CREDENTIALS 0x31 +#define KLDAP_INSUFFICIENT_ACCESS 0x32 + +#define KLDAP_SERVICE_ERROR(n) KLDAP_RANGE((n),0x33,0x36) /* 51-54 */ + +#define KLDAP_BUSY 0x33 +#define KLDAP_UNAVAILABLE 0x34 +#define KLDAP_UNWILLING_TO_PERFORM 0x35 +#define KLDAP_LOOP_DETECT 0x36 + +#define KLDAP_UPDATE_ERROR(n) KLDAP_RANGE((n),0x40,0x47) /* 64-69,71 */ + +#define KLDAP_NAMING_VIOLATION 0x40 +#define KLDAP_OBJECT_CLASS_VIOLATION 0x41 +#define KLDAP_NOT_ALLOWED_ON_NONLEAF 0x42 +#define KLDAP_NOT_ALLOWED_ON_RDN 0x43 +#define KLDAP_ALREADY_EXISTS 0x44 +#define KLDAP_NO_OBJECT_CLASS_MODS 0x45 +#define KLDAP_RESULTS_TOO_LARGE 0x46 /* CLDAP */ +#define KLDAP_AFFECTS_MULTIPLE_DSAS 0x47 + +#define KLDAP_OTHER 0x50 + +/* LCUP operation codes (113-117) - not implemented */ +#define KLDAP_CUP_RESOURCES_EXHAUSTED 0x71 +#define KLDAP_CUP_SECURITY_VIOLATION 0x72 +#define KLDAP_CUP_INVALID_DATA 0x73 +#define KLDAP_CUP_UNSUPPORTED_SCHEME 0x74 +#define KLDAP_CUP_RELOAD_REQUIRED 0x75 + +/* Cancel operation codes (118-121) */ +#define KLDAP_CANCELLED 0x76 +#define KLDAP_NO_SUCH_OPERATION 0x77 +#define KLDAP_TOO_LATE 0x78 + +#define KLDAP_CANNOT_CANCEL 0x79 + +/* Assertion control (122) */ +#define KLDAP_ASSERTION_FAILED 0x7A + +/* Experimental result codes */ +#define KLDAP_E_ERROR(n) KLDAP_RANGE((n),0x1000,0x3FFF) + +/* LDAP Sync (4096) */ +#define KLDAP_SYNC_REFRESH_REQUIRED 0x1000 + +/* Private Use result codes */ +#define KLDAP_X_ERROR(n) KLDAP_RANGE((n),0x4000,0xFFFF) + +#define KLDAP_X_SYNC_REFRESH_REQUIRED 0x4100 /* defunct */ +#define KLDAP_X_ASSERTION_FAILED 0x410f /* defunct */ + +/* for the LDAP No-Op control */ +#define KLDAP_X_NO_OPERATION 0x410e + +/** API Error Codes + * + * Based on draft-ietf-ldap-c-api-xx + * but with new negative code values + */ +#define KLDAP_API_ERROR(n) ((n)<0) +#define KLDAP_API_RESULT(n) ((n)<=0) + +#define KLDAP_SERVER_DOWN (-1) +#define KLDAP_LOCAL_ERROR (-2) +#define KLDAP_ENCODING_ERROR (-3) +#define KLDAP_DECODING_ERROR (-4) +#define KLDAP_TIMEOUT (-5) +#define KLDAP_AUTH_UNKNOWN (-6) +#define KLDAP_FILTER_ERROR (-7) +#define KLDAP_USER_CANCELLED (-8) +#define KLDAP_PARAM_ERROR (-9) +#define KLDAP_NO_MEMORY (-10) +#define KLDAP_CONNECT_ERROR (-11) +#define KLDAP_NOT_SUPPORTED (-12) +#define KLDAP_CONTROL_NOT_FOUND (-13) +#define KLDAP_NO_RESULTS_RETURNED (-14) +#define KLDAP_MORE_RESULTS_TO_RETURN (-15) /* Obsolete */ +#define KLDAP_CLIENT_LOOP (-16) +#define KLDAP_REFERRAL_LIMIT_EXCEEDED (-17) + +/* + * KLDAP Specific + */ + +#define KLDAP_SASL_ERROR -0xff + +#endif //KLDAP_DEFS_H diff --git a/src/ldapdn.cpp b/src/ldapdn.cpp new file mode 100644 index 0000000..6bcdf84 --- /dev/null +++ b/src/ldapdn.cpp @@ -0,0 +1,210 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapdn.h" + +#include + +#include "ldap_debug.h" + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapDN::LdapDNPrivate +{ +public: + LdapDNPrivate() : m_dn() {} + ~LdapDNPrivate() {} + + bool isValidRDNString(const QString &rdn) const; + QStringList splitOnNonEscapedChar(const QString &rdn, QChar ch) const; + + QString m_dn; +}; + +bool LdapDN::LdapDNPrivate::isValidRDNString(const QString &rdn) const +{ + qCDebug(LDAP_LOG) << "Testing rdn:" << rdn; + + // If it is a muli-valued rdn, split it into its constituent parts + const QStringList rdnParts = splitOnNonEscapedChar(rdn, QLatin1Char('+')); + const int rdnPartsSize(rdnParts.size()); + if (rdnPartsSize > 1) { + for (int i = 0; i < rdnPartsSize; i++) { + if (!isValidRDNString(rdnParts.at(i))) { + return false; + } + } + return true; + } + + // Split the rdn into the attribute name and value parts + QStringList components = rdn.split(QLatin1Char('=')); + + // We should have exactly two parts + if (components.size() != 2) { + return false; + } + + return true; +} + +QStringList LdapDN::LdapDNPrivate::splitOnNonEscapedChar(const QString &str, QChar ch) const +{ + QStringList strParts; + int index = 0; + int searchFrom = 0; + int strPartStartIndex = 0; + while ((index = str.indexOf(ch, searchFrom)) != -1) { + const QChar prev = str[std::max(0, index - 1)]; + if (prev != QLatin1Char('\\')) { + // Found a component of a multi-valued RDN + //qCDebug(LDAP_LOG) << "Found" << ch << "at index" << index; + QString tmp = str.mid(strPartStartIndex, index - strPartStartIndex); + //qCDebug(LDAP_LOG) << "Adding part:" << tmp; + strParts.append(tmp); + strPartStartIndex = index + 1; + } + + searchFrom = index + 1; + } + + // Add on the part after the last found delimeter + QString tmp = str.mid(strPartStartIndex); + //qCDebug(LDAP_LOG) << "Adding part:" << tmp; + strParts.append(tmp); + + return strParts; +} + +LdapDN::LdapDN() + : d(new LdapDNPrivate) +{ + +} + +LdapDN::LdapDN(const QString &dn) + : d(new LdapDNPrivate) +{ + d->m_dn = dn; +} + +LdapDN::LdapDN(const LdapDN &that) + : d(new LdapDNPrivate) +{ + *d = *that.d; +} + +LdapDN &LdapDN::operator=(const LdapDN &that) +{ + if (this == &that) { + return *this; + } + + *d = *that.d; + return *this; +} + +LdapDN::~LdapDN() +{ + delete d; +} + +void LdapDN::clear() +{ + d->m_dn.clear(); +} + +bool LdapDN::isEmpty() const +{ + return d->m_dn.isEmpty(); +} + +QString LdapDN::toString() const +{ + return d->m_dn; +} + +QString LdapDN::toString(int depth) const +{ + QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + if (depth >= rdns.size()) { + return QString(); + } + + // Construct a DN down to the requested depth + QString dn; + for (int i = depth; i >= 0; i--) { + dn += rdns.at(rdns.size() - 1 - i) + QLatin1Char(','); + qCDebug(LDAP_LOG) << "dn =" << dn; + } + dn = dn.left(dn.length() - 1); // Strip off the extraneous comma + + return dn; +} + +QString LdapDN::rdnString() const +{ + /** \TODO We should move this into the d pointer as we calculate rdns quite a lot */ + QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + return rdns.at(0); +} + +QString LdapDN::rdnString(int depth) const +{ + QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + if (depth >= rdns.size()) { + return QString(); + } + return rdns.at(rdns.size() - 1 - depth); +} + +bool LdapDN::isValid() const +{ + qCDebug(LDAP_LOG) << "Testing dn:" << d->m_dn; + + // Break the string into rdn's + const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + + // Test to see if each rdn is valid + const int rdnsSize(rdns.size()); + for (int i = 0; i < rdnsSize; i++) { + if (!d->isValidRDNString(rdns.at(i))) { + return false; + } + } + + return true; +} + +int LdapDN::depth() const +{ + QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + return rdns.size(); +} + +bool LdapDN::operator == (const LdapDN &rhs) const +{ + return d->m_dn == rhs.d->m_dn; +} + +bool LdapDN::operator != (const LdapDN &rhs) const +{ + return d->m_dn != rhs.d->m_dn; +} diff --git a/src/ldapdn.h b/src/ldapdn.h new file mode 100644 index 0000000..939e6eb --- /dev/null +++ b/src/ldapdn.h @@ -0,0 +1,89 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPDN_H +#define KLDAP_LDAPDN_H + +#include "kldap_export.h" + +#include + +namespace KLDAP +{ + +class KLDAP_EXPORT LdapDN +{ +public: + explicit LdapDN(); + explicit LdapDN(const QString &dn); + + LdapDN(const LdapDN &that); + LdapDN &operator=(const LdapDN &that); + + ~LdapDN(); + + void clear(); + + bool isEmpty() const; + + /** + * \returns A QString representing the DN. + */ + QString toString() const; + + /** + * \param depth The depth of the DN to return using a zero-based index. + * \returns A QString representing the DN levels deep in the directory. + */ + QString toString(int depth) const; + + /** + * \returns A QString representing the RDN of this DN. + */ + QString rdnString() const; + + /** + * \param depth The depth of the RDN to return using a zero-based index. + * \returns A QString representing the RDN levels deep in the directory. + */ + QString rdnString(int depth) const; + + /** + * \returns True if this is a valid DN, false otherwise. + */ + bool isValid() const; + + /** + * \returns The depth of this DN in the directory. + */ + int depth() const; + + bool operator == (const LdapDN &rhs) const; + + bool operator != (const LdapDN &rhs) const; + +private: + class LdapDNPrivate; + LdapDNPrivate *const d; +}; + +} + +#endif diff --git a/src/ldapmodel.cpp b/src/ldapmodel.cpp new file mode 100644 index 0000000..994e352 --- /dev/null +++ b/src/ldapmodel.cpp @@ -0,0 +1,319 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapmodel.h" +#include "ldapmodel_p.h" +#include "ldapmodelnode_p.h" +#include "ldapsearch.h" + +#include "ldap_debug.h" +#include + +using namespace KLDAP; + +LdapModel::LdapModel(QObject *parent) + : QAbstractItemModel(parent), + m_d(new LdapModelPrivate(this)) +{ + m_d->createConnections(); +} + +LdapModel::LdapModel(LdapConnection &connection, QObject *parent) + : QAbstractItemModel(parent), + m_d(new LdapModelPrivate(this, connection)) +{ + m_d->createConnections(); + + // Populate items from the root object to that representing the baseDN + m_d->populateRootToBaseDN(); +} + +LdapModel::~LdapModel() +{ + delete m_d; +} + +void LdapModel::setConnection(LdapConnection &connection) +{ + m_d->setConnection(connection); + + // Refresh the model + m_d->recreateRootItem(); + + // Populate the root object by searching the baseDN + m_d->populateRootToBaseDN(); +} + +QModelIndex LdapModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) { + return QModelIndex(); + } + + LdapModelNode *childItem = static_cast(child.internalPointer()); + LdapModelDNNode *parentItem = childItem->parent(); + + if (parentItem == m_d->rootNode()) { + return QModelIndex(); + } + + return createIndex(parentItem->row(), 0, parentItem); +} + +QModelIndex LdapModel::index(int row, int col, const QModelIndex &parent) const +{ + // Retrieve a pointer to the parent item + LdapModelDNNode *parentItem; + if (!parent.isValid()) { + parentItem = m_d->rootNode(); + } else { + parentItem = static_cast(parent.internalPointer()); + } + + LdapModelNode *childItem = parentItem->child(row); + if (childItem) { + return createIndex(row, col, childItem); + } + qCDebug(LDAP_LOG) << "Could not create valid index for row =" << row << ", col =" << col; + return QModelIndex(); +} + +QVariant LdapModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + if (role == Qt::DisplayRole) { + // This is what gets displayed by the view delegates. + LdapModelNode *node = static_cast(index.internalPointer()); + if (node->nodeType() == LdapModelNode::DN) { + LdapModelDNNode *dn = static_cast(node); + if (index.column() == 0) { + return dn->dn().rdnString(); + } else { + return QVariant(); + } + } else { + LdapModelAttrNode *attr = static_cast(node); + if (index.column() == 0) { + return QVariant(attr->attributeName()); + } else { + return QVariant(QLatin1String(attr->attributeData().constData())); + } + } + } else if (role == NodeTypeRole) { + LdapModelNode *node = static_cast(index.internalPointer()); + return QVariant(int(node->nodeType())); + } + + /** \todo Include support for nice decorative icons dependent upon + the objectClass + other role data. */ + /** \todo Include support for other roles as needed */ + + return QVariant(); +} + +bool LdapModel::setData(const QModelIndex &index, + const QVariant &value, + int role) +{ + Q_UNUSED(index); + Q_UNUSED(value); + Q_UNUSED(role); + return false; +} + +QVariant LdapModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + if (section == 0) { + return i18n("Attribute"); + } else { + return i18n("Value"); + } + } + + return QVariant(); +} + +Qt::ItemFlags LdapModel::flags(const QModelIndex &index) const +{ + /** \TODO Read-only for now, make read-write upon request */ + if (!index.isValid()) { + return Qt::ItemIsEnabled; + } + + return Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); +} + +int LdapModel::columnCount(const QModelIndex &parent) const +{ + LdapModelDNNode *parentNode = + parent.isValid() ? static_cast(parent.internalPointer()) : m_d->rootNode(); + return parentNode->columnCount(); +} + +int LdapModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) { + return 0; + } + + const LdapModelDNNode *parentNode = + parent.isValid() ? static_cast(parent.internalPointer()) : m_d->rootNode(); + return parentNode->childCount(); +} + +bool LdapModel::hasChildren(const QModelIndex &parent) const +{ + // We return true unless the item has been populated and we are able to do a definitive test + const LdapModelNode *node = parent.isValid() ? + static_cast(parent.internalPointer()) : + m_d->rootNode(); + + if (node->nodeType() != LdapModelNode::DN) { + return false; + } + + const LdapModelDNNode *parentNode = static_cast(node); + if (!parent.isValid() || parentNode->isPopulated()) { + return parentNode->childCount() > 0; + } + return true; +} + +bool LdapModel::canFetchMore(const QModelIndex &parent) const +{ + const LdapModelDNNode *parentNode = + parent.isValid() ? static_cast(parent.internalPointer()) : m_d->rootNode(); + return !parentNode->isPopulated(); +} + +void LdapModel::fetchMore(const QModelIndex &parent) +{ + LdapModelDNNode *parentNode = + parent.isValid() ? static_cast(parent.internalPointer()) : m_d->rootNode(); + + // Search for the immediate children of parentItem. + m_d->searchResults().clear(); + m_d->setSearchType(LdapModelPrivate::ChildObjects, parentNode); + m_d->search(parentNode->dn(), // DN to search from + LdapUrl::One, // What to search + QString()); // Attributes to retrieve + parentNode->setPopulated(true); +} + +bool LdapModel::insertRows(int row, int count, + const QModelIndex &parent) +{ + Q_UNUSED(row); + Q_UNUSED(count); + Q_UNUSED(parent); + return false; +} + +bool LdapModel::removeRows(int row, int count, + const QModelIndex &parent) +{ + Q_UNUSED(row); + Q_UNUSED(count); + Q_UNUSED(parent); + return false; +} + +void LdapModel::sort(int column, Qt::SortOrder order) +{ + Q_UNUSED(column); + Q_UNUSED(order); +} + +Qt::DropActions LdapModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QMimeData *LdapModel::mimeData(const QModelIndexList &indexes) const +{ + Q_UNUSED(indexes); + return Q_NULLPTR; +} + +bool LdapModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + /** \todo Implement drag and drop for LdapModel */ + Q_UNUSED(data); + Q_UNUSED(action); + Q_UNUSED(row); + Q_UNUSED(column); + Q_UNUSED(parent); + return false; +} + +bool LdapModel::hasChildrenOfType(const QModelIndex &parent, LdapDataType type) const +{ + // Map from LdapDataType to our internal NodeType + LdapModelNode::NodeType nodeType; + switch (type) { + case Attribute: + nodeType = LdapModelNode::Attr; + break; + + case DistinguishedName: + default: + nodeType = LdapModelNode::DN; + break; + } + + const LdapModelNode *node = parent.isValid() ? + static_cast(parent.internalPointer()) : + m_d->rootNode(); + + const LdapModelDNNode *parentNode = static_cast(node); + if (!parent.isValid() || parentNode->isPopulated()) { + // Check to see if the parent has any children of the specified type + const QList &children = parentNode->children(); + foreach (LdapModelNode *child, children) { + if (child->nodeType() == nodeType) { + return true; + } + } + + // Either there are no children or only children of a different type + return false; + } + + // If the node is not populated or is the root node (invalid), then return + // true to be on the safe side. + return true; +} + +void LdapModel::revert() +{ + +} + +bool LdapModel::submit() +{ + return false; +} + +#include "moc_ldapmodel.cpp" diff --git a/src/ldapmodel.h b/src/ldapmodel.h new file mode 100644 index 0000000..3bd4495 --- /dev/null +++ b/src/ldapmodel.h @@ -0,0 +1,217 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPMODEL_H +#define KLDAP_LDAPMODEL_H + +#include + +#include "ldapconnection.h" +#include "ldapobject.h" +#include "kldap_export.h" + +namespace KLDAP +{ + +/** + * A ModelView interface to an LDAP tree. At present the model is read only. Editing is + * planned for a future release. + * + * This class is best used in conjunction with an LdapStructureProxyModel object for + * displaying the structure of an LDAP tree, and with LdapAttributeProxyModel for + * displaying the attributes of particular objects within the tree. + * + * \author Sean Harmer + */ +class KLDAP_EXPORT LdapModel : public QAbstractItemModel +{ + Q_OBJECT +public: + enum Roles { + NodeTypeRole = Qt::UserRole + 1 + }; + + enum LdapDataType { + DistinguishedName = 0, + Attribute + }; + + /** + * Constructs an LdapModel. You should set a connection for the model to use with + * setConnection(). Clients of this class should connect a slot to the ready() signal + * before setting this model onto a view. + * @param parent the parent QObject + * \see setConnection() + * \see ready() + */ + explicit LdapModel(QObject *parent = Q_NULLPTR); + /** + * Constructs an LdapModel. Clients of this class should connect a slot to the ready() + * signal before setting this model onto a view. + * @param connection the Ldap connection to use in model construction + * @param parent the parent QObject + * \see setConnection() + * \see ready() + */ + explicit LdapModel(LdapConnection &connection, QObject *parent = Q_NULLPTR); + virtual ~LdapModel(); + + /** + * Set the connection that the model should use. + * @param connection the model connection to set + * \see LdapConnection + * \see LdapUrl + */ + void setConnection(LdapConnection &connection); + + // + // Implement the usual QAbstractItemModel interface + // + /** + * Reimplemented from QAbstractItemModel::index(). + */ + QModelIndex index(int row, int col, const QModelIndex &parent) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::parent(). + */ + QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::data(). + */ + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::setData(). This is a placeholder for when + * LdapModel beomes writeable and always returns false. + */ + bool setData(const QModelIndex &index, + const QVariant &value, + int role = Qt::EditRole) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::headerData(). + */ + QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::flags(). + */ + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::columnCount(). + */ + int columnCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::rowCount(). + */ + int rowCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::hasChildren(). + */ + bool hasChildren(const QModelIndex &parent) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::canFetchMore(). + */ + bool canFetchMore(const QModelIndex &parent) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::fetchMore(). + */ + void fetchMore(const QModelIndex &parent) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::insertRows(). This is a placeholder for when + * LdapModel beomes writeable and always returns false. + */ + bool insertRows(int row, int count, + const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::removeRows(). This is a placeholder for when + * LdapModel beomes writeable and always returns false. + */ + bool removeRows(int row, int count, + const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::removeRows(). The default implementation + * does nothing. + */ + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) Q_DECL_OVERRIDE; + + // + // Drag and drop support + // + /** + * Reimplemented from QAbstractItemModel::supportedDropActions(). The default + * implementation returns Qt::MoveAction. + */ + Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::mimedata(). This is a placeholder for when + * LdapModel beomes writeable and always returns 0. + */ + QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::dropMimedata(). This is a placeholder for when + * LdapModel beomes writeable and always returns false. + */ + bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) Q_DECL_OVERRIDE; + + // + // Other public utility functions + // + /** + * Checks to see if the item referenced by \p parent has any children of + * the type \p type. If the item has not been populated by fetchMore() yet, + * then this function returns true. + * + * \see fetchMore() + * \param parent Index to the item to query. + * \param type The type of child item to search for. + */ + bool hasChildrenOfType(const QModelIndex &parent, LdapDataType type) const; + +public Q_SLOTS: + /** + * Reimplemented from QAbstractItemModel::revert(). This is a placeholder for when + * LdapModel beomes writeable. This implementation does nothing. + */ + void revert() Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::revert(). This is a placeholder for when + * LdapModel beomes writeable. This implementation does nothing and returns false. + */ + bool submit() Q_DECL_OVERRIDE; + +Q_SIGNALS: + /** + * The ready() signal is emitted when the model is ready for use by other components. + * When the model is first created and a connection is set, the model queries the + * LDAP server for its base DN and automatically creates items down to that level. + * This requires the event loop to be running. This signal indicates that this process + * has completed and the model can now be set onto views or queried directly from code. + */ + void ready(); + +private: + class LdapModelPrivate; + LdapModelPrivate *const m_d; + + Q_PRIVATE_SLOT(m_d, void gotSearchResult(KLDAP::LdapSearch *)) + Q_PRIVATE_SLOT(m_d, void gotSearchData(KLDAP::LdapSearch *, const KLDAP::LdapObject &)) +}; + +} +#endif diff --git a/src/ldapmodel_p.cpp b/src/ldapmodel_p.cpp new file mode 100644 index 0000000..ad0bcb9 --- /dev/null +++ b/src/ldapmodel_p.cpp @@ -0,0 +1,212 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapmodel_p.h" +#include "ldapmodelnode_p.h" +#include "ldapsearch.h" + +#include "ldap_debug.h" + +using namespace KLDAP; + +LdapModel::LdapModelPrivate::LdapModelPrivate(LdapModel *parent) + : m_parent(parent), + m_root(new LdapModelDNNode), + m_search(new LdapSearch), + m_searchResultObjects(), + m_baseDN(), + m_searchType(NotSearching), + m_searchItem(Q_NULLPTR) +{ +} + +LdapModel::LdapModelPrivate::LdapModelPrivate(LdapModel *parent, LdapConnection &connection) + : m_parent(parent), + m_root(new LdapModelDNNode), + m_search(new LdapSearch(connection)), + m_searchResultObjects(), + m_baseDN(), + m_searchType(NotSearching), + m_searchItem(Q_NULLPTR) +{ +} + +LdapModel::LdapModelPrivate::~LdapModelPrivate() +{ + delete m_root; + + delete m_search; +} + +void LdapModel::LdapModelPrivate::setConnection(LdapConnection &connection) +{ + m_search->setConnection(connection); +} + +bool LdapModel::LdapModelPrivate::search(const LdapDN &searchBase, + LdapUrl::Scope scope, + const QString &filter, + const QStringList &attributes, + int pagesize) +{ + return m_search->search(searchBase, scope, filter, attributes, pagesize); +} + +void LdapModel::LdapModelPrivate::setSearchType(SearchType t, LdapModelDNNode *item) +{ + //qCDebug(LDAP_LOG) << "item =" << item; + m_searchType = t; + m_searchItem = item; +} + +void LdapModel::LdapModelPrivate::recreateRootItem() +{ + //qCDebug(LDAP_LOG); + delete m_root; + m_root = new LdapModelDNNode; + //qCDebug(LDAP_LOG) << "&m_root =" << &m_root; +} + +void LdapModel::LdapModelPrivate::createConnections() +{ + connect(search(), SIGNAL(result(KLDAP::LdapSearch*)), + m_parent, SLOT(gotSearchResult(KLDAP::LdapSearch*))); + connect(search(), SIGNAL(data(KLDAP::LdapSearch*,KLDAP::LdapObject)), + m_parent, SLOT(gotSearchData(KLDAP::LdapSearch*,KLDAP::LdapObject))); +} + +void LdapModel::LdapModelPrivate::populateRootToBaseDN() +{ + //qCDebug(LDAP_LOG); + + if (baseDN().isEmpty()) { + // Query the server for the base DN + //qCDebug(LDAP_LOG) << "Searching for the baseDN"; + setSearchType(LdapModelPrivate::NamingContexts, rootNode()); + search(LdapDN(), LdapUrl::Base, QString(), QStringList() << QStringLiteral("namingContexts")); + return; + } + + // Start a search for the details of the baseDN object + //qCDebug(LDAP_LOG) << "Searching for attributes of the baseDN"; + searchResults().clear(); + setSearchType(LdapModelPrivate::BaseDN, rootNode()); + search(baseDN(), LdapUrl::Base, QString(), QStringList() << QStringLiteral("dn") << QStringLiteral("objectClass")); +} + +void LdapModel::LdapModelPrivate::gotSearchResult(KLDAP::LdapSearch *search) +{ + Q_UNUSED(search); + qCDebug(LDAP_LOG); + + switch (searchType()) { + case LdapModelPrivate::NamingContexts: { + // Set the baseDN + QString baseDN; + if (!searchResults().isEmpty() && + searchResults().at(0).hasAttribute(QStringLiteral("namingContexts"))) { + baseDN = QLatin1String(searchResults().at(0).value(QStringLiteral("namingContexts"))); + //qCDebug(LDAP_LOG) << "Found baseDN =" << baseDN; + } + setBaseDN(LdapDN(baseDN)); + + // Flag that we are no longer searching for the baseDN + setSearchType(LdapModelPrivate::NotSearching); + + // Populate the root item + populateRootToBaseDN(); + + break; + } + case LdapModelPrivate::BaseDN: { + //qCDebug(LDAP_LOG) << "Found details of the baseDN object." + // << "Creating objects down to this level."; + + // Get the baseDN LdapObject + LdapObject baseDNObj = searchResults().at(0); + + // How many levels of items do we need to create? + int depth = baseDNObj.dn().depth(); + + // Create items that represent objects down to the baseDN + LdapModelDNNode *parent = rootNode(); + LdapModelDNNode *item = Q_NULLPTR; + for (int i = 0; i < depth; ++i) { + QString dn = baseDN().toString(i); + qCDebug(LDAP_LOG) << "Creating item for DN :" << dn; + + //LdapObject obj( dn ); + item = new LdapModelDNNode(parent, LdapDN(dn)); + parent = item; + } + + // Store the search result + if (item) { + item->setLdapObject(baseDNObj); + } + + // Flag that we are no longer searching + setSearchType(LdapModelPrivate::NotSearching); + //emit( layoutChanged() ); + + // Let the world know we are ready for action + emit m_parent->ready(); + + break; + } + case LdapModelPrivate::ChildObjects: { + //qCDebug(LDAP_LOG) << "Found" << searchResults().size() << "child objects"; + + if (searchResults().size() != 0) { + // Create an index for the soon-to-be-a-parent item + LdapModelDNNode *parentNode = searchItem(); + int r = parentNode->row(); + QModelIndex parentIndex = m_parent->createIndex(r, 0, parentNode); + + m_parent->beginInsertRows(parentIndex, 0, searchResults().size()); + for (int i = 0; i < searchResults().size(); i++) { + LdapObject object = searchResults().at(i); + LdapModelDNNode *item = new LdapModelDNNode(parentNode, object.dn()); + item->setLdapObject(object); + } + + m_parent->endInsertRows(); + emit m_parent->layoutChanged(); + } + + // Flag that we are no longer searching + setSearchType(LdapModelPrivate::NotSearching); + + break; + } + default: + break; + } +} + +void LdapModel::LdapModelPrivate::gotSearchData(KLDAP::LdapSearch *search, + const KLDAP::LdapObject &obj) +{ + Q_UNUSED(search); + //qCDebug(LDAP_LOG); + //qCDebug(LDAP_LOG) << "Object:"; + //qCDebug(LDAP_LOG) << obj.toString(); + searchResults().append(obj); +} diff --git a/src/ldapmodel_p.h b/src/ldapmodel_p.h new file mode 100644 index 0000000..57dc250 --- /dev/null +++ b/src/ldapmodel_p.h @@ -0,0 +1,121 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPMODELPRIVATE_H +#define KLDAP_LDAPMODELPRIVATE_H + +#include "ldapconnection.h" +#include "ldapdn.h" +#include "ldapmodel.h" +#include "ldapobject.h" + +namespace KLDAP +{ + +class LdapModelDNNode; +class LdapSearch; + +/** + * @internal + */ +class Q_DECL_HIDDEN LdapModel::LdapModelPrivate +{ +public: + enum SearchType { + NotSearching = 0, + NamingContexts, + BaseDN, + ChildObjects + }; + + explicit LdapModelPrivate(LdapModel *parent); + explicit LdapModelPrivate(LdapModel *parent, LdapConnection &connection); + + ~LdapModelPrivate(); + + void setConnection(LdapConnection &connection); + + bool search(const LdapDN &searchBase, + LdapUrl::Scope scope = LdapUrl::Sub, + const QString &filter = QString(), + const QStringList &attributes = QStringList(), + int pagesize = 0); + + LdapModelDNNode *rootNode() + { + return m_root; + } + LdapSearch *search() + { + return m_search; + } + + LdapObjects &searchResults() + { + return m_searchResultObjects; + } + const LdapObjects &searchResults() const + { + return m_searchResultObjects; + } + + void recreateRootItem(); + + void setBaseDN(const LdapDN &baseDN) + { + m_baseDN = baseDN; + } + LdapDN &baseDN() + { + return m_baseDN; + } + const LdapDN &baseDN() const + { + return m_baseDN; + } + + void setSearchType(SearchType t, LdapModelDNNode *item = Q_NULLPTR); + + SearchType searchType() + { + return m_searchType; + } + LdapModelDNNode *searchItem() + { + return m_searchItem; + } + + void createConnections(); + void populateRootToBaseDN(); + void gotSearchResult(KLDAP::LdapSearch *search); + void gotSearchData(KLDAP::LdapSearch *search, const KLDAP::LdapObject &obj); + +private: + LdapModel *m_parent; + LdapModelDNNode *m_root; + LdapSearch *m_search; + LdapObjects m_searchResultObjects; + LdapDN m_baseDN; + SearchType m_searchType; + LdapModelDNNode *m_searchItem; +}; + +} +#endif diff --git a/src/ldapmodelnode_p.cpp b/src/ldapmodelnode_p.cpp new file mode 100644 index 0000000..7a4fd25 --- /dev/null +++ b/src/ldapmodelnode_p.cpp @@ -0,0 +1,132 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapmodelnode_p.h" + +#include "ldap_debug.h" + +using namespace KLDAP; + +LdapModelNode::LdapModelNode(LdapModelDNNode *parent) + : m_parent(parent), + m_isPopulated(false) +{ + if (m_parent) { + m_parent->appendChild(this); + } +} + +LdapModelNode::~LdapModelNode() +{ + +} + +LdapModelDNNode *LdapModelNode::parent() +{ + return m_parent; +} + +int LdapModelNode::row() const +{ + if (m_parent) { + return m_parent->children().indexOf(const_cast(this)); + } + return 0; +} + +// +// LdapModelDNNode imlpementation +// + +LdapModelDNNode::LdapModelDNNode(LdapModelDNNode *parent, + const LdapDN &dn) + : LdapModelNode(parent), + m_childItems(), + m_dn(dn) +{ + qCDebug(LDAP_LOG) << "Creating DN =" << m_dn.toString(); +} + +LdapModelDNNode::~LdapModelDNNode() +{ + qDeleteAll(m_childItems); +} + +void LdapModelDNNode::appendChild(LdapModelNode *pItem) +{ + m_childItems.append(pItem); + setPopulated(true); +} + +LdapModelNode *LdapModelDNNode::child(int row) +{ + return m_childItems.value(row); +} + +void LdapModelDNNode::setLdapObject(const LdapObject &object) +{ + // Remember whether this item is populated or not + bool populated = isPopulated(); + + const LdapAttrMap &attrs = object.attributes(); + /* + int attributeCount = 0; + for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) { + attributeCount += (*it).size(); + } + + for ( int i = 0; i < attributeCount; i++ ) + { + LdapModelNode* node = new LdapModelAttrNode( this, QString::number( i ) ); + Q_UNUSED( node ); + } + */ + LdapAttrMap::ConstIterator end(attrs.constEnd()); + for (LdapAttrMap::ConstIterator it = attrs.constBegin(); it != end; ++it) { + const QString attr = it.key(); + LdapAttrValue::ConstIterator end2((*it).constEnd()); + for (LdapAttrValue::ConstIterator it2 = (*it).constBegin(); it2 != end2; ++it2) { + LdapModelNode *node = new LdapModelAttrNode(this, attr, *it2); + Q_UNUSED(node); + } + } + + // Reset the populated flag so that we don't stop the model querying for children + setPopulated(populated); +} + +// +// LdapModelAttrNode imlpementation +// + +LdapModelAttrNode::LdapModelAttrNode(LdapModelDNNode *parent, + const QString &attrName, + const QByteArray &attrData) + : LdapModelNode(parent), + m_attrName(attrName), + m_attrData(attrData) +{ + qCDebug(LDAP_LOG) << "Creating Name =" << m_attrName << " Data =" << m_attrData; +} + +LdapModelAttrNode::~LdapModelAttrNode() +{ + +} diff --git a/src/ldapmodelnode_p.h b/src/ldapmodelnode_p.h new file mode 100644 index 0000000..b469fbc --- /dev/null +++ b/src/ldapmodelnode_p.h @@ -0,0 +1,151 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPMODELNODE_P_H +#define KLDAP_LDAPMODELNODE_P_H + +#include +#include +#include +#include + +#include "ldapdn.h" +#include "ldapobject.h" +#include "kldap_export.h" + +namespace KLDAP +{ + +class LdapModelDNNode; + +/** + * @internal + */ +class LdapModelNode +{ +public: + explicit LdapModelNode(LdapModelDNNode *parent = Q_NULLPTR); + virtual ~LdapModelNode(); + + enum NodeType { + DN, + Attr + }; + + virtual NodeType nodeType() const = 0; + + LdapModelDNNode *parent(); + int columnCount() const + { + return 2; + } + int row() const; + + void setPopulated(bool b) + { + m_isPopulated = b; + } + bool isPopulated() const + { + return m_isPopulated; + } + +private: + LdapModelDNNode *m_parent; + bool m_isPopulated; +}; + +/** + * @internal + */ +class LdapModelDNNode : public LdapModelNode +{ +public: + explicit LdapModelDNNode(LdapModelDNNode *parent = Q_NULLPTR, + const LdapDN &dn = LdapDN()); + ~LdapModelDNNode(); + + LdapModelNode::NodeType nodeType() const Q_DECL_OVERRIDE + { + return LdapModelNode::DN; + } + + void appendChild(LdapModelNode *pItem); + LdapModelNode *child(int row); + int childCount() const + { + return m_childItems.size(); + } + const QList &children() const + { + return m_childItems; + } + + const LdapDN &dn() const + { + return m_dn; + } + + /** + * Creates child LdapModelAttrNode object to store \p object's attributes + * and adds them as children of this node. + * + * \param The LdapObject to store in this node. + */ + void setLdapObject(const LdapObject &object); + +private: + QList m_childItems; + LdapDN m_dn; +}; + +/** + * @internal + */ +class LdapModelAttrNode : public LdapModelNode +{ +public: + explicit LdapModelAttrNode(LdapModelDNNode *parent = Q_NULLPTR, + const QString &attrName = QString(), + const QByteArray &attrData = QByteArray()); + ~LdapModelAttrNode(); + + LdapModelNode::NodeType nodeType() const Q_DECL_OVERRIDE + { + return LdapModelNode::Attr; + } + + const QString &attributeName() + { + return m_attrName; + } + const QByteArray &attributeData() + { + return m_attrData; + } + +private: + QString m_attrName; + QByteArray m_attrData; +}; + +} + +#endif diff --git a/src/ldapobject.cpp b/src/ldapobject.cpp new file mode 100644 index 0000000..21f21d2 --- /dev/null +++ b/src/ldapobject.cpp @@ -0,0 +1,151 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapobject.h" +#include "ldif.h" + +#include + +using namespace KLDAP; + +class LdapObjectPrivate : public QSharedData +{ +public: + LdapObjectPrivate() + { + } + + LdapObjectPrivate(const LdapObjectPrivate &other) + : QSharedData(other) + { + mDn = other.mDn; + mAttrs = other.mAttrs; + } + + LdapDN mDn; + LdapAttrMap mAttrs; +}; + +LdapObject::LdapObject() + : d(new LdapObjectPrivate) +{ +} + +LdapObject::LdapObject(const QString &dn) + : d(new LdapObjectPrivate) +{ + d->mDn = LdapDN(dn); +} + +LdapObject::~LdapObject() +{ +} + +LdapObject::LdapObject(const LdapObject &that) + : d(that.d) +{ +} + +LdapObject &LdapObject::operator=(const LdapObject &that) +{ + if (this != &that) { + d = that.d; + } + + return *this; +} + +void LdapObject::setDn(const LdapDN &dn) +{ + d->mDn = dn; +} + +void LdapObject::setDn(const QString &dn) +{ + d->mDn = LdapDN(dn); +} + +void LdapObject::setAttributes(const LdapAttrMap &attrs) +{ + d->mAttrs = attrs; +} + +LdapDN LdapObject::dn() const +{ + return d->mDn; +} + +const LdapAttrMap &LdapObject::attributes() const +{ + return d->mAttrs; +} + +QString LdapObject::toString() const +{ + QString result = QStringLiteral("dn: %1\n").arg(d->mDn.toString()); + LdapAttrMap::ConstIterator end(d->mAttrs.constEnd()); + for (LdapAttrMap::ConstIterator it = d->mAttrs.constBegin(); it != end; ++it) { + const QString attr = it.key(); + LdapAttrValue::ConstIterator end2((*it).constEnd()); + for (LdapAttrValue::ConstIterator it2 = (*it).constBegin(); it2 != end2; ++it2) { + result += QString::fromUtf8(Ldif::assembleLine(attr, *it2, 76)) + QLatin1Char('\n'); + } + } + return result; +} + +void LdapObject::clear() +{ + d->mDn.clear(); + d->mAttrs.clear(); +} + +void LdapObject::setValues(const QString &attributeName, const LdapAttrValue &values) +{ + d->mAttrs[ attributeName ] = values; +} + +void LdapObject::addValue(const QString &attributeName, const QByteArray &value) +{ + d->mAttrs[ attributeName ].append(value); +} + +LdapAttrValue LdapObject::values(const QString &attributeName) const +{ + if (hasAttribute(attributeName)) { + return d->mAttrs.value(attributeName); + } else { + return LdapAttrValue(); + } +} + +QByteArray LdapObject::value(const QString &attributeName) const +{ + if (hasAttribute(attributeName)) { + return d->mAttrs.value(attributeName).first(); + } else { + return QByteArray(); + } +} + +bool LdapObject::hasAttribute(const QString &attributeName) const +{ + return d->mAttrs.contains(attributeName); +} diff --git a/src/ldapobject.h b/src/ldapobject.h new file mode 100644 index 0000000..2e632f9 --- /dev/null +++ b/src/ldapobject.h @@ -0,0 +1,117 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPOBJECT_H +#define KLDAP_LDAPOBJECT_H + +#include +#include +#include +#include +class LdapObjectPrivate; + +#include "ldapdn.h" +#include "kldap_export.h" + +namespace KLDAP +{ + +typedef QList LdapAttrValue; +typedef QMap LdapAttrMap; + +/** + * @brief + * This class represents an LDAP Object +*/ +class KLDAP_EXPORT LdapObject +{ +public: + LdapObject(); + explicit LdapObject(const QString &dn); + virtual ~LdapObject(); + + LdapObject(const LdapObject &that); + LdapObject &operator=(const LdapObject &that); + + /** + * Returns the text presentation (LDIF format) of the object. + */ + QString toString() const; + + /** + * Clears the name and attributes of the object. + */ + void clear(); + /** + * Sets the Distinguished Name of the object. + */ + void setDn(const LdapDN &dn); + /** + * Sets the Distinguished Name of the object. + */ + void setDn(const QString &dn); + /** + * Sets the attributes and attribute values of the object. + */ + void setAttributes(const LdapAttrMap &attrs); + /** + * Sets the given attribute values. If the given attribute not exists, + * then it's created, if exists, it's overwritten. + * @param attributeName the attribute name for which to set values + * @param values the values of attribute to set + */ + void setValues(const QString &attributeName, const LdapAttrValue &values); + /** + * Adds the given value to the specified attribute. If the given attribute + * not exists, then it's created. + * @param attributeName the attribute for which to add a value + * @param value the attribute value to add + */ + void addValue(const QString &attributeName, const QByteArray &value); + /** + * Return the Distinguished Name of the object. + */ + LdapDN dn() const; + /** + * Returns the attributes and their values. + */ + const LdapAttrMap &attributes() const; + /** + * Returns all values of the attribute with the given name. + */ + LdapAttrValue values(const QString &attributeName) const; + /** + * Returns the first value of the attribute with the given name + * or an empty byte array if the attribute does not exists. + */ + QByteArray value(const QString &attributeName) const; + /** + * Returns true if the given attributethe exists, false otherwise. + */ + bool hasAttribute(const QString &attributeName) const; + +private: + QSharedDataPointer d; +}; + +typedef QVector LdapObjects; +} + +#endif diff --git a/src/ldapoperation.cpp b/src/ldapoperation.cpp new file mode 100644 index 0000000..e4df81d --- /dev/null +++ b/src/ldapoperation.cpp @@ -0,0 +1,1324 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapoperation.h" +#include "kldap_config.h" + +#include "ldap_debug.h" + +#include + +#include + +//for struct timeval +#if defined(HAVE_SYS_TIME_H) +# include +#elif defined(_WIN32) +# include +#endif + +#ifdef SASL2_FOUND +#include +#endif + +#ifdef LDAP_FOUND +# ifndef HAVE_WINLDAP_H +# include +# include +# else +# include +# endif // HAVE_WINLDAP_H +#endif // LDAP_FOUND + +#include "ldapdefs.h" + +using namespace KLDAP; + +#ifdef LDAP_FOUND +static void extractControls(LdapControls &ctrls, LDAPControl **pctrls); +#endif // LDAP_FOUND + +/* + Returns the difference between msecs and elapsed. If msecs is -1, + however, -1 is returned. +*/ +static int kldap_timeout_value(int msecs, int elapsed) +{ + if (msecs == -1) { + return -1; + } + + int timeout = msecs - elapsed; + return timeout < 0 ? 0 : timeout; +} + +class Q_DECL_HIDDEN LdapOperation::LdapOperationPrivate +{ +public: + LdapOperationPrivate(); + ~LdapOperationPrivate(); +#ifdef LDAP_FOUND + int processResult(int rescode, LDAPMessage *msg); + int bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data, bool async); +#endif + LdapControls mClientCtrls, mServerCtrls, mControls; + LdapObject mObject; + QByteArray mExtOid, mExtData; + QByteArray mServerCred; + QString mMatchedDn; + QList mReferrals; + + LdapConnection *mConnection; +}; + +LdapOperation::LdapOperation() + : d(new LdapOperationPrivate) +{ + d->mConnection = Q_NULLPTR; +} + +LdapOperation::LdapOperation(LdapConnection &conn) + : d(new LdapOperationPrivate) +{ + setConnection(conn); +} + +LdapOperation::~LdapOperation() +{ + delete d; +} + +void LdapOperation::setConnection(LdapConnection &conn) +{ + d->mConnection = &conn; +} + +LdapConnection &LdapOperation::connection() +{ + return *d->mConnection; +} + +void LdapOperation::setClientControls(const LdapControls &ctrls) +{ + d->mClientCtrls = ctrls; +} + +void LdapOperation::setServerControls(const LdapControls &ctrls) +{ + d->mServerCtrls = ctrls; +} + +LdapControls LdapOperation::clientControls() const +{ + return d->mClientCtrls; +} + +LdapControls LdapOperation::serverControls() const +{ + return d->mServerCtrls; +} + +LdapObject LdapOperation::object() const +{ + return d->mObject; +} + +LdapControls LdapOperation::controls() const +{ + return d->mControls; +} + +QByteArray LdapOperation::extendedOid() const +{ + return d->mExtOid; +} + +QByteArray LdapOperation::extendedData() const +{ + return d->mExtData; +} + +QString LdapOperation::matchedDn() const +{ + return d->mMatchedDn; +} + +QList LdapOperation::referrals() const +{ + return d->mReferrals; +} + +QByteArray LdapOperation::serverCred() const +{ + return d->mServerCred; +} + +LdapOperation::LdapOperationPrivate::LdapOperationPrivate() + : mConnection(Q_NULLPTR) +{ +} + +LdapOperation::LdapOperationPrivate::~LdapOperationPrivate() +{ +} + +#ifdef LDAP_FOUND + +#ifdef SASL2_FOUND +static int kldap_sasl_interact(sasl_interact_t *interact, LdapOperation::SASL_Data *data) +{ + if (data->proc) { + for (; interact->id != SASL_CB_LIST_END; interact++) { + switch (interact->id) { + case SASL_CB_GETREALM: + data->creds.fields |= LdapOperation::SASL_Realm; + break; + case SASL_CB_AUTHNAME: + data->creds.fields |= LdapOperation::SASL_Authname; + break; + case SASL_CB_PASS: + data->creds.fields |= LdapOperation::SASL_Password; + break; + case SASL_CB_USER: + data->creds.fields |= LdapOperation::SASL_Authzid; + break; + } + } + int retval; + if ((retval = data->proc(data->creds, data->data))) { + return retval; + } + } + + QString value; + + while (interact->id != SASL_CB_LIST_END) { + value.clear(); + switch (interact->id) { + case SASL_CB_GETREALM: + value = data->creds.realm; + qCDebug(LDAP_LOG) << "SASL_REALM=" << value; + break; + case SASL_CB_AUTHNAME: + value = data->creds.authname; + qCDebug(LDAP_LOG) << "SASL_AUTHNAME=" << value; + break; + case SASL_CB_PASS: + value = data->creds.password; + qCDebug(LDAP_LOG) << "SASL_PASSWD=[hidden]"; + break; + case SASL_CB_USER: + value = data->creds.authzid; + qCDebug(LDAP_LOG) << "SASL_AUTHZID=" << value; + break; + } + if (value.isEmpty()) { + interact->result = Q_NULLPTR; + interact->len = 0; + } else { + interact->result = strdup(value.toUtf8()); + interact->len = strlen((const char *)interact->result); + } + interact++; + } + return KLDAP_SUCCESS; +} +#endif + +int LdapOperation::LdapOperationPrivate::bind(const QByteArray &creds, + SASL_Callback_Proc *saslproc, + void *data, bool async) +{ + Q_ASSERT(mConnection); + LDAP *ld = (LDAP *) mConnection->handle(); + LdapServer server; + server = mConnection->server(); + + int ret; + + if (server.auth() == LdapServer::SASL) { +#if defined( SASL2_FOUND ) && !defined( HAVE_WINLDAP_H ) + sasl_conn_t *saslconn = (sasl_conn_t *)mConnection->saslHandle(); + sasl_interact_t *client_interact = Q_NULLPTR; + const char *out = Q_NULLPTR; + uint outlen; + const char *mechusing = Q_NULLPTR; + struct berval ccred, *scred; + int saslresult; + QByteArray sdata = creds; + + QString mech = server.mech(); + if (mech.isEmpty()) { + mech = QStringLiteral("DIGEST-MD5"); + } + + SASL_Data sasldata; + sasldata.proc = saslproc; + sasldata.data = data; + sasldata.creds.fields = 0; + sasldata.creds.realm = server.realm(); + sasldata.creds.authname = server.user(); + sasldata.creds.authzid = server.bindDn(); + sasldata.creds.password = server.password(); + + do { + if (sdata.isEmpty()) { + do { + saslresult = sasl_client_start(saslconn, mech.toLatin1(), + &client_interact, &out, &outlen, &mechusing); + + if (saslresult == SASL_INTERACT) { + if (kldap_sasl_interact(client_interact, &sasldata) != KLDAP_SUCCESS) { + return KLDAP_SASL_ERROR; + } + } + qCDebug(LDAP_LOG) << "sasl_client_start mech: " + << mechusing << " outlen " << outlen + << " result: " << saslresult; + } while (saslresult == SASL_INTERACT); + if (saslresult != SASL_CONTINUE && saslresult != SASL_OK) { + return KLDAP_SASL_ERROR; + } + + } else { + qCDebug(LDAP_LOG) << "sasl_client_step"; + do { + saslresult = sasl_client_step(saslconn, sdata.data(), sdata.size(), + &client_interact, &out, &outlen); + if (saslresult == SASL_INTERACT) { + if (kldap_sasl_interact(client_interact, &sasldata) != KLDAP_SUCCESS) { + return KLDAP_SASL_ERROR; + } + } + } while (saslresult == SASL_INTERACT); + qCDebug(LDAP_LOG) << "sasl_client_step result" << saslresult; + if (saslresult != SASL_CONTINUE && saslresult != SASL_OK) { + return KLDAP_SASL_ERROR; + } + } + + ccred.bv_val = (char *) out; + ccred.bv_len = outlen; + + if (async) { + qCDebug(LDAP_LOG) << "ldap_sasl_bind"; + int msgid; + ret = + ldap_sasl_bind(ld, server.bindDn().toUtf8().data(), mech.toLatin1(), + &ccred, Q_NULLPTR, Q_NULLPTR, &msgid); + if (ret == 0) { + ret = msgid; + } + qCDebug(LDAP_LOG) << "ldap_sasl_bind msgid" << ret; + } else { + qCDebug(LDAP_LOG) << "ldap_sasl_bind_s"; + ret = + ldap_sasl_bind_s(ld, server.bindDn().toUtf8().data(), mech.toLatin1(), + &ccred, Q_NULLPTR, Q_NULLPTR, &scred); + qCDebug(LDAP_LOG) << "ldap_sasl_bind_s ret" << ret; + if (scred) { + sdata = QByteArray(scred->bv_val, scred->bv_len); + } else { + sdata = QByteArray(); + } + } + } while (!async && ret == KLDAP_SASL_BIND_IN_PROGRESS); +#else + qCritical() << "SASL authentication is not available " + << "(re-compile kldap with cyrus-sasl and OpenLDAP development)."; + return KLDAP_SASL_ERROR; +#endif + } else { //simple auth + QByteArray bindname, pass; + struct berval ccred; + if (server.auth() == LdapServer::Simple) { + bindname = server.bindDn().toUtf8(); + pass = server.password().toUtf8(); + } + ccred.bv_val = pass.data(); + ccred.bv_len = pass.size(); + qCDebug(LDAP_LOG) << "binding to server, bindname: " << bindname << " password: *****"; + + if (async) { + qCDebug(LDAP_LOG) << "ldap_sasl_bind (simple)"; +#ifndef HAVE_WINLDAP_H + int msgid = 0; + ret = ldap_sasl_bind(ld, bindname.data(), Q_NULLPTR, &ccred, Q_NULLPTR, Q_NULLPTR, &msgid); + if (ret == 0) { + ret = msgid; + } +#else + ret = ldap_simple_bind(ld, bindname.data(), pass.data()); +#endif + } else { + qCDebug(LDAP_LOG) << "ldap_sasl_bind_s (simple)"; +#ifndef HAVE_WINLDAP_H + ret = ldap_sasl_bind_s(ld, bindname.data(), Q_NULLPTR, &ccred, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); +#else + ret = ldap_simple_bind_s(ld, bindname.data(), pass.data()); +#endif + } + } + return ret; +} + +int LdapOperation::LdapOperationPrivate::processResult(int rescode, LDAPMessage *msg) +{ + //qCDebug(LDAP_LOG); + int retval; + LDAP *ld = (LDAP *) mConnection->handle(); + + qCDebug(LDAP_LOG) << "rescode: " << rescode; + switch (rescode) { + case RES_SEARCH_ENTRY: { + //qCDebug(LDAP_LOG) << "Found search entry"; + mObject.clear(); + LdapAttrMap attrs; + char *name; + struct berval **bvals; + BerElement *entry; + + char *dn = ldap_get_dn(ld, msg); + mObject.setDn(QString::fromUtf8(dn)); + ldap_memfree(dn); + + // iterate over the attributes + name = ldap_first_attribute(ld, msg, &entry); + while (name != Q_NULLPTR) { + // print the values + bvals = ldap_get_values_len(ld, msg, name); + LdapAttrValue values; + if (bvals) { + for (int i = 0; bvals[i] != Q_NULLPTR; i++) { + char *val = bvals[i]->bv_val; + unsigned long len = bvals[i]->bv_len; + values.append(QByteArray(val, len)); + } + ldap_value_free_len(bvals); + } + attrs[ QString::fromLatin1(name) ] = values; + ldap_memfree(name); + + // next attribute + name = ldap_next_attribute(ld, msg, entry); + } + ber_free(entry, 0); + mObject.setAttributes(attrs); + break; + } + case RES_SEARCH_REFERENCE: + // Will only get this if following references is disabled. ignore it + rescode = 0; + break; + case RES_EXTENDED: { + char *retoid; + struct berval *retdata; + retval = ldap_parse_extended_result(ld, msg, &retoid, &retdata, 0); + if (retval != KLDAP_SUCCESS) { + ldap_msgfree(msg); + return -1; + } + mExtOid = retoid ? QByteArray(retoid) : QByteArray(); + mExtData = retdata ? QByteArray(retdata->bv_val, retdata->bv_len) : QByteArray(); + ldap_memfree(retoid); + ber_bvfree(retdata); + break; + } + case RES_BIND: { + struct berval *servercred = Q_NULLPTR; +#ifndef HAVE_WINLDAP_H + // FIXME: Error handling Winldap does not have ldap_parse_sasl_bind_result + retval = ldap_parse_sasl_bind_result(ld, msg, &servercred, 0); +#else + retval = KLDAP_SUCCESS; +#endif + if (retval != KLDAP_SUCCESS && retval != KLDAP_SASL_BIND_IN_PROGRESS) { + qCDebug(LDAP_LOG) << "RES_BIND error: " << retval; + ldap_msgfree(msg); + return -1; + } + qCDebug(LDAP_LOG) << "RES_BIND rescode" << rescode << "retval:" << retval; + if (servercred) { + mServerCred = QByteArray(servercred->bv_val, servercred->bv_len); + ber_bvfree(servercred); + } else { + mServerCred = QByteArray(); + } + break; + } + default: { + LDAPControl **serverctrls = Q_NULLPTR; + char *matcheddn = Q_NULLPTR, *errmsg = Q_NULLPTR; + char **referralsp; + int errcodep; + retval = + ldap_parse_result(ld, msg, &errcodep, &matcheddn, &errmsg, &referralsp, + &serverctrls, 0); + qCDebug(LDAP_LOG) << "rescode" << rescode << "retval:" << retval + << "matcheddn:" << matcheddn << "errcode:" + << errcodep << "errmsg:" << errmsg; + if (retval != KLDAP_SUCCESS) { + ldap_msgfree(msg); + return -1; + } + mControls.clear(); + if (serverctrls) { + extractControls(mControls, serverctrls); + ldap_controls_free(serverctrls); + } + mReferrals.clear(); + if (referralsp) { + char **tmp = referralsp; + while (*tmp) { + mReferrals.append(QByteArray(*tmp)); + ldap_memfree(*tmp); + tmp++; + } + ldap_memfree((char *) referralsp); + } + mMatchedDn.clear(); + if (matcheddn) { + mMatchedDn = QString::fromUtf8(matcheddn); + ldap_memfree(matcheddn); + } + if (errmsg) { + ldap_memfree(errmsg); + } + } + } + + ldap_msgfree(msg); + + return rescode; +} + +static void addModOp(LDAPMod ***pmods, int mod_type, const QString &attr, + const QByteArray *value = Q_NULLPTR) +{ + // qCDebug(LDAP_LOG) << "type:" << mod_type << "attr:" << attr << + // "value:" << QString::fromUtf8(value,value.size()) << + // "size:" << value.size(); + LDAPMod **mods; + + mods = *pmods; + + uint i = 0; + + if (mods == Q_NULLPTR) { + mods = (LDAPMod **)malloc(2 * sizeof(LDAPMod *)); + mods[ 0 ] = (LDAPMod *)malloc(sizeof(LDAPMod)); + mods[ 1 ] = Q_NULLPTR; + memset(mods[ 0 ], 0, sizeof(LDAPMod)); + } else { + while (mods[ i ] != Q_NULLPTR && + (strcmp(attr.toUtf8(), mods[i]->mod_type) != 0 || + (mods[ i ]->mod_op & ~LDAP_MOD_BVALUES) != mod_type)) { + i++; + } + + if (mods[ i ] == Q_NULLPTR) { + mods = (LDAPMod **)realloc(mods, (i + 2) * sizeof(LDAPMod *)); + if (mods == Q_NULLPTR) { + qCritical() << "addModOp: realloc"; + return; + } + mods[ i + 1 ] = Q_NULLPTR; + mods[ i ] = (LDAPMod *) malloc(sizeof(LDAPMod)); + memset(mods[ i ], 0, sizeof(LDAPMod)); + } + } + + mods[ i ]->mod_op = mod_type | LDAP_MOD_BVALUES; + if (mods[ i ]->mod_type == Q_NULLPTR) { + mods[ i ]->mod_type = strdup(attr.toUtf8()); + } + + *pmods = mods; + + if (value == Q_NULLPTR) { + return; + } + + int vallen = value->size(); + BerValue *berval; + berval = (BerValue *) malloc(sizeof(BerValue)); + berval -> bv_len = vallen; + if (vallen > 0) { + berval -> bv_val = (char *) malloc(vallen); + memcpy(berval -> bv_val, value->data(), vallen); + } else { + berval -> bv_val = Q_NULLPTR; + } + + if (mods[ i ] -> mod_vals.modv_bvals == Q_NULLPTR) { + mods[ i ]->mod_vals.modv_bvals = + (BerValue **) malloc(sizeof(BerValue *) * 2); + mods[ i ]->mod_vals.modv_bvals[ 0 ] = berval; + mods[ i ]->mod_vals.modv_bvals[ 1 ] = Q_NULLPTR; +// qCDebug(LDAP_LOG) << "new bervalue struct" << attr << value; + } else { + uint j = 0; + while (mods[ i ]->mod_vals.modv_bvals[ j ] != Q_NULLPTR) { + j++; + } + mods[ i ]->mod_vals.modv_bvals = + (BerValue **)realloc(mods[ i ]->mod_vals.modv_bvals, + (j + 2) * sizeof(BerValue *)); + if (mods[ i ]->mod_vals.modv_bvals == Q_NULLPTR) { + qCritical() << "addModOp: realloc"; + free(berval); + return; + } + mods[ i ]->mod_vals.modv_bvals[ j ] = berval; + mods[ i ]->mod_vals.modv_bvals[ j + 1 ] = Q_NULLPTR; + qCDebug(LDAP_LOG) << j << ". new bervalue"; + } +} + +static void addControlOp(LDAPControl ***pctrls, const QString &oid, + const QByteArray &value, bool critical) +{ + LDAPControl **ctrls; + LDAPControl *ctrl = (LDAPControl *) malloc(sizeof(LDAPControl)); + + ctrls = *pctrls; + + qCDebug(LDAP_LOG) << "oid:'" << oid << "' val: '" << value << "'"; + int vallen = value.size(); + ctrl->ldctl_value.bv_len = vallen; + if (vallen) { + ctrl->ldctl_value.bv_val = (char *) malloc(vallen); + memcpy(ctrl->ldctl_value.bv_val, value.data(), vallen); + } else { + ctrl->ldctl_value.bv_val = Q_NULLPTR; + } + ctrl->ldctl_iscritical = critical; + ctrl->ldctl_oid = strdup(oid.toUtf8()); + + uint i = 0; + + if (ctrls == Q_NULLPTR) { + ctrls = (LDAPControl **)malloc(2 * sizeof(LDAPControl *)); + ctrls[ 0 ] = Q_NULLPTR; + ctrls[ 1 ] = Q_NULLPTR; + } else { + while (ctrls[ i ] != Q_NULLPTR) { + i++; + } + ctrls[ i + 1 ] = Q_NULLPTR; + ctrls = + (LDAPControl **)realloc(ctrls, (i + 2) * sizeof(LDAPControl *)); + } + ctrls[ i ] = ctrl; + *pctrls = ctrls; +} + +static void createControls(LDAPControl ***pctrls, const LdapControls &ctrls) +{ + for (int i = 0; i < ctrls.count(); ++i) { + addControlOp(pctrls, ctrls[i].oid(), ctrls[i].value(), ctrls[i].critical()); + } +} + +static void extractControls(LdapControls &ctrls, LDAPControl **pctrls) +{ + LDAPControl *ctrl; + LdapControl control; + int i = 0; + + while (pctrls[i]) { + ctrl = pctrls[ i ]; + control.setOid(QString::fromUtf8(ctrl->ldctl_oid)); + control.setValue(QByteArray(ctrl->ldctl_value.bv_val, + ctrl->ldctl_value.bv_len)); + control.setCritical(ctrl->ldctl_iscritical); + ctrls.append(control); + i++; + } +} + +int LdapOperation::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data) +{ + return d->bind(creds, saslproc, data, true); +} + +int LdapOperation::bind_s(SASL_Callback_Proc *saslproc, void *data) +{ + return d->bind(QByteArray(), saslproc, data, false); +} + +int LdapOperation::search(const LdapDN &base, LdapUrl::Scope scope, + const QString &filter, const QStringList &attributes) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + char **attrs = Q_NULLPTR; + int msgid; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int count = attributes.count(); + if (count > 0) { + attrs = static_cast(malloc((count + 1) * sizeof(char *))); + for (int i = 0; i < count; i++) { + attrs[i] = strdup(attributes.at(i).toUtf8()); + } + attrs[count] = Q_NULLPTR; + } + + int lscope = LDAP_SCOPE_BASE; + switch (scope) { + case LdapUrl::Base: + lscope = LDAP_SCOPE_BASE; + break; + case LdapUrl::One: + lscope = LDAP_SCOPE_ONELEVEL; + break; + case LdapUrl::Sub: + lscope = LDAP_SCOPE_SUBTREE; + break; + } + + qCDebug(LDAP_LOG) << "asyncSearch() base=\"" << base.toString() + << "\" scope=" << (int)scope + << "filter=\"" << filter + << "\" attrs=" << attributes; + int retval = + ldap_search_ext(ld, base.toString().toUtf8().data(), lscope, + filter.isEmpty() ? QByteArray("objectClass=*").data() : + filter.toUtf8().data(), + attrs, 0, serverctrls, clientctrls, Q_NULLPTR, + d->mConnection->sizeLimit(), &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + // free the attributes list again + if (count > 0) { + for (int i = 0; i < count; i++) { + free(attrs[i]); + } + free(attrs); + } + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::add(const LdapObject &object) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + int msgid; + LDAPMod **lmod = Q_NULLPTR; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (LdapAttrMap::ConstIterator it = object.attributes().begin(); + it != object.attributes().end(); ++it) { + QString attr = it.key(); + for (LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) { + addModOp(&lmod, 0, attr, &(*it2)); + } + } + + int retval = + ldap_add_ext(ld, object.dn().toString().toUtf8().data(), lmod, serverctrls, + clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::add_s(const LdapObject &object) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPMod **lmod = Q_NULLPTR; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (LdapAttrMap::ConstIterator it = object.attributes().begin(); + it != object.attributes().end(); ++it) { + QString attr = it.key(); + for (LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) { + addModOp(&lmod, 0, attr, &(*it2)); + } + } + + int retval = + ldap_add_ext_s(ld, object.dn().toString().toUtf8().data(), lmod, serverctrls, + clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + return retval; +} + +int LdapOperation::add(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + int msgid; + LDAPMod **lmod = Q_NULLPTR; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, 0, ops[i].attr, &ops[i].values[j]); + } + } + + int retval = + ldap_add_ext(ld, dn.toString().toUtf8().data(), lmod, serverctrls, + clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::add_s(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPMod **lmod = Q_NULLPTR; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, 0, ops[i].attr, &ops[i].values[j]); + } + } + qCDebug(LDAP_LOG) << dn.toString(); + int retval = + ldap_add_ext_s(ld, dn.toString().toUtf8().data(), lmod, serverctrls, + clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + return retval; +} + +int LdapOperation::rename(const LdapDN &dn, const QString &newRdn, + const QString &newSuperior, bool deleteold) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + int msgid; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_rename(ld, dn.toString().toUtf8().data(), newRdn.toUtf8().data(), + newSuperior.isEmpty() ? (char *) Q_NULLPTR : newSuperior.toUtf8().data(), + deleteold, serverctrls, clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::rename_s(const LdapDN &dn, const QString &newRdn, + const QString &newSuperior, bool deleteold) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_rename_s(ld, dn.toString().toUtf8().data(), newRdn.toUtf8().data(), + newSuperior.isEmpty() ? (char *) Q_NULLPTR : newSuperior.toUtf8().data(), + deleteold, serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::del(const LdapDN &dn) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + int msgid; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = + ldap_delete_ext(ld, dn.toString().toUtf8().data(), serverctrls, clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::del_s(const LdapDN &dn) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_delete_ext_s(ld, dn.toString().toUtf8().data(), serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::modify(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + int msgid; + LDAPMod **lmod = Q_NULLPTR; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + int mtype = 0; + switch (ops[i].type) { + case Mod_None: + mtype = 0; + break; + case Mod_Add: + mtype = LDAP_MOD_ADD; + break; + case Mod_Replace: + mtype = LDAP_MOD_REPLACE; + break; + case Mod_Del: + mtype = LDAP_MOD_DELETE; + break; + } + addModOp(&lmod, mtype, ops[i].attr, Q_NULLPTR); + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, mtype, ops[i].attr, &ops[i].values[j]); + } + } + + int retval = + ldap_modify_ext(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::modify_s(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPMod **lmod = Q_NULLPTR; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + int mtype = 0; + switch (ops[i].type) { + case Mod_None: + mtype = 0; + break; + case Mod_Add: + mtype = LDAP_MOD_ADD; + break; + case Mod_Replace: + mtype = LDAP_MOD_REPLACE; + break; + case Mod_Del: + mtype = LDAP_MOD_DELETE; + break; + } + addModOp(&lmod, mtype, ops[i].attr, Q_NULLPTR); + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, mtype, ops[i].attr, &ops[i].values[j]); + } + } + + int retval = + ldap_modify_ext_s(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + return retval; +} + +int LdapOperation::compare(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + int msgid; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = value.size(); + BerValue *berval; + berval = (BerValue *) malloc(sizeof(BerValue)); + berval -> bv_val = (char *) malloc(vallen); + berval -> bv_len = vallen; + memcpy(berval -> bv_val, value.data(), vallen); + + int retval = ldap_compare_ext(ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval, + serverctrls, clientctrls, &msgid); + + ber_bvfree(berval); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = value.size(); + BerValue *berval; + berval = (BerValue *) malloc(sizeof(BerValue)); + berval -> bv_val = (char *) malloc(vallen); + berval -> bv_len = vallen; + memcpy(berval -> bv_val, value.data(), vallen); + + int retval = ldap_compare_ext_s(ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval, + serverctrls, clientctrls); + + ber_bvfree(berval); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::exop(const QString &oid, const QByteArray &data) +{ + Q_ASSERT(d->mConnection); +#if defined(HAVE_LDAP_EXTENDED_OPERATION) && defined(HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE) + LDAP *ld = (LDAP *) d->mConnection->handle(); + int msgid; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = data.size(); + BerValue *berval; + berval = (BerValue *) malloc(sizeof(BerValue)); + berval -> bv_val = (char *) malloc(vallen); + berval -> bv_len = vallen; + memcpy(berval -> bv_val, data.data(), vallen); + + int retval = ldap_extended_operation(ld, oid.toUtf8().data(), berval, + serverctrls, clientctrls, &msgid); + + ber_bvfree(berval); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +#else + qCritical() << "Your LDAP client libraries don't support extended operations."; + return -1; +#endif +} + +int LdapOperation::exop_s(const QString &oid, const QByteArray &data) +{ +#if defined(HAVE_LDAP_EXTENDED_OPERATION) && defined(HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE) + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + BerValue *retdata; + char *retoid; + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = data.size(); + BerValue *berval; + berval = (BerValue *) malloc(sizeof(BerValue)); + berval -> bv_val = (char *) malloc(vallen); + berval -> bv_len = vallen; + memcpy(berval -> bv_val, data.data(), vallen); + + int retval = ldap_extended_operation_s(ld, oid.toUtf8().data(), berval, + serverctrls, clientctrls, &retoid, &retdata); + + ber_bvfree(berval); + ber_bvfree(retdata); + free(retoid); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +#else + qCritical() << "Your LDAP client libraries don't support extended operations."; + return -1; +#endif +} + +int LdapOperation::abandon(int id) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPControl **serverctrls = Q_NULLPTR, **clientctrls = Q_NULLPTR; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_abandon_ext(ld, id, serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::waitForResult(int id, int msecs) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *) d->mConnection->handle(); + + LDAPMessage *msg; + int rescode; + + QTime stopWatch; + stopWatch.start(); + int attempt(1); + int timeout(0); + + do { + // Calculate the timeout value to use and assign it to a timeval structure + // see man select (2) for details + timeout = kldap_timeout_value(msecs, stopWatch.elapsed()); + qCDebug(LDAP_LOG) << "(" << id << "," << msecs + << "): Waiting" << timeout + << "msecs for result. Attempt #" << attempt++; + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + // Wait for a result + rescode = ldap_result(ld, id, 0, timeout < 0 ? Q_NULLPTR : &tv, &msg); + if (rescode == -1) { + return -1; + } + // Act on the return code + if (rescode != 0) { + // Some kind of result is available for processing + return d->processResult(rescode, msg); + } + } while (msecs == -1 || stopWatch.elapsed() < msecs); + + return 0; //timeout +} + +#else + +int LdapOperation::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::bind_s(SASL_Callback_Proc *saslproc, void *data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::search(const LdapDN &base, LdapUrl::Scope scope, + const QString &filter, const QStringList &attributes) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add(const LdapObject &object) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add_s(const LdapObject &object) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add_s(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::rename(const LdapDN &dn, const QString &newRdn, + const QString &newSuperior, bool deleteold) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::rename_s(const LdapDN &dn, const QString &newRdn, + const QString &newSuperior, bool deleteold) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::del(const LdapDN &dn) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::del_s(const LdapDN &dn) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::modify(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::modify_s(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::compare(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::exop(const QString &oid, const QByteArray &data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::exop_s(const QString &oid, const QByteArray &data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::waitForResult(int id, int msecs) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::abandon(int id) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +#endif diff --git a/src/ldapoperation.h b/src/ldapoperation.h new file mode 100644 index 0000000..9f4860a --- /dev/null +++ b/src/ldapoperation.h @@ -0,0 +1,300 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPOPERATION_H +#define KLDAP_LDAPOPERATION_H + +#include "kldap_export.h" +#include "ldapconnection.h" +#include "ldapcontrol.h" +#include "ldapobject.h" +#include "ldapdn.h" +#include "ldapserver.h" +#include "ldapurl.h" + +#include +#include +#include + +namespace KLDAP +{ + +/** + * @brief + * This class allows sending an ldap operation + * (search, rename, modify, delete, compare, exop) to an LDAP server. + */ +class KLDAP_EXPORT LdapOperation +{ +public: + typedef enum { + Mod_None, Mod_Add, Mod_Replace, Mod_Del + } ModType; + + typedef enum { + RES_BIND = 0x61, + RES_SEARCH_ENTRY = 0x64, + RES_SEARCH_REFERENCE = 0x73, + RES_SEARCH_RESULT = 0x65, + RES_MODIFY = 0x67, + RES_ADD = 0x69, + RES_DELETE = 0x69, + RES_MODDN = 0x6d, + RES_COMPARE = 0x6f, + RES_EXTENDED = 0x78, + RES_EXTENDED_PARTIAL = 0x79 + } ResultType; + + typedef struct { + ModType type; + QString attr; + QList values; + } ModOp ; + + typedef QVector ModOps; + + enum SASL_Fields { + SASL_Authname = 0x1, + SASL_Authzid = 0x2, + SASL_Realm = 0x4, + SASL_Password = 0x8 + }; + + struct SASL_Credentials { + int fields; + QString authname; + QString authzid; + QString realm; + QString password; + }; + + typedef int (SASL_Callback_Proc)(SASL_Credentials &cred, void *data); + + struct SASL_Data { + SASL_Callback_Proc *proc; + void *data; + SASL_Credentials creds; + }; + + LdapOperation(); + LdapOperation(LdapConnection &conn); + virtual ~LdapOperation(); + + /** + * Sets the connection object. Without living connection object, + * LDAP operations are not possible. + * @param the connection object to set + */ + void setConnection(LdapConnection &conn); + /** + * Returns the connection object. + */ + LdapConnection &connection(); + /** + * Sets the client controls which will sent with each operation. + */ + void setClientControls(const LdapControls &ctrls); + /** + * Sets the server controls which will sent with each operation. + */ + void setServerControls(const LdapControls &ctrls); + /** + * Returns the client controls (which set by setClientControls()). + */ + LdapControls clientControls() const; + /** + * Returns the server controls (which set by setServerControls()). + */ + LdapControls serverControls() const; + + /** + * Binds to the server which specified in the connection object. + * Can do simple or SASL bind. Returns a message id if successful, negative value if not. + */ + int bind(const QByteArray &creds = QByteArray(), + SASL_Callback_Proc *saslproc = Q_NULLPTR, void *data = Q_NULLPTR); + + /** + * Binds to the server which specified in the connection object. + * Can do simple or SASL bind. This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + int bind_s(SASL_Callback_Proc *saslproc = Q_NULLPTR, void *data = Q_NULLPTR); + + /** + * Starts a search operation with the given base DN, scope, filter and + * result attributes. Returns a message id if successful, -1 if not. + */ + int search(const LdapDN &base, LdapUrl::Scope scope, + const QString &filter, const QStringList &attrs); + /** + * Starts an addition operation. + * Returns a message id if successful, -1 if not. + * @param object the additional operation to start + */ + int add(const LdapObject &object); + /** + * Adds the specified object to the LDAP database. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + * @param object the object to add to LDAP database + */ + int add_s(const LdapObject &object); + /** + * Starts an addition operation. This version accepts ModOps not LdapObject. + * Returns a message id if successful, -1 if not. + * @param dn the LdapDN operation to start + * @param ops the ModOps operation to start + */ + int add(const LdapDN &dn, const ModOps &ops); + /** + * Adds the specified object to the LDAP database. This version accepts ModOps not LdapObject. + * This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + * @param dn the LdapDN object to add + * @param ops the ModOps object to add + */ + int add_s(const LdapDN &dn, const ModOps &ops); + /** + * Starts a modrdn operation on given DN, changing its RDN to newRdn, + * changing its parent to newSuperior (if it's not empty), and deletes + * the old dn if deleteold is true. + * Returns a message id if successful, -1 if not. + */ + int rename(const LdapDN &dn, const QString &newRdn, + const QString &newSuperior, bool deleteold = true); + /** + * Performs a modrdn operation on given DN, changing its RDN to newRdn, + * changing its parent to newSuperior (if it's not empty), and deletes + * the old dn if deleteold is true. This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + int rename_s(const LdapDN &dn, const QString &newRdn, + const QString &newSuperior, bool deleteold = true); + /** + * Starts a delete operation on the given DN. + * Returns a message id if successful, -1 if not. + */ + int del(const LdapDN &dn); + /** + * Deletes the given DN. This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + * @param dn the dn to delete + */ + int del_s(const LdapDN &dn); + /** + * Starts a modify operation on the given DN. + * Returns a message id if successful, -1 if not. + * @param dn the DN to start modify operation on + */ + int modify(const LdapDN &dn, const ModOps &ops); + /** + * Performs a modify operation on the given DN. + * This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + int modify_s(const LdapDN &dn, const ModOps &ops); + /** + * Starts a compare operation on the given DN, compares the specified + * attribute with the given value. + * Returns a message id if successful, -1 if not. + */ + int compare(const LdapDN &dn, const QString &attr, const QByteArray &value); + /** + * Performs a compare operation on the given DN, compares the specified + * attribute with the given value. This is the synchronous version. + * Returns KLDAP_COMPARE_TRUE if the entry contains the attribute value + * and KLDAP_COMPARE_FALSE if it does not. Otherwise, some error code + * is returned. + */ + int compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value); + /** + * Starts an extended operation specified with oid and data. + * Returns a message id if successful, -1 if not. + */ + int exop(const QString &oid, const QByteArray &data); + /** + * Performs an extended operation specified with oid and data. + * This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + int exop_s(const QString &oid, const QByteArray &data); + /** + * Abandons a long-running operation. Requires the message id. + */ + int abandon(int id); + /** + * Waits for up to \p msecs milliseconds for a result message from the LDAP + * server. If \p msecs is -1, then this function will block indefinitely. + * If \p msecs is 0, then this function will return immediately, that is it + * will perform a poll for a result message. + * + * Returns the type of the result LDAP message (RES_XXX constants). + * -1 if error occurred, 0 if the timeout value elapsed. Note! + * Return code -1 means that fetching the message resulted in error, + * not the LDAP operation error. Call connection().ldapErrorCode() to + * determine if the operation succeeded. + */ + int waitForResult(int id, int msecs = -1); + /** + * Returns the result object if result() returned RES_SEARCH_ENTRY. + */ + LdapObject object() const; + /** + * Returns the server controls from the returned ldap message (grabbed + * by result()). + */ + LdapControls controls() const; + /** + * Returns the OID of the extended operation response (result + * returned RES_EXTENDED). + */ + QByteArray extendedOid() const; + /** + * Returns the data from the extended operation response (result + * returned RES_EXTENDED). + */ + QByteArray extendedData() const; + /** + * The server might supply a matched DN string in the message indicating + * how much of a name in a request was recognized. This can be grabbed by + * matchedDn(). + */ + QString matchedDn() const; + /** + * This function returns the referral strings from the parsed message + * (if any). + */ + QList referrals() const; + /** + * Returns the server response for a bind request (result + * returned RES_BIND). + */ + QByteArray serverCred() const; + +private: + class LdapOperationPrivate; + LdapOperationPrivate *const d; + + Q_DISABLE_COPY(LdapOperation) +}; + +} + +#endif diff --git a/src/ldapsearch.cpp b/src/ldapsearch.cpp new file mode 100644 index 0000000..f427615 --- /dev/null +++ b/src/ldapsearch.cpp @@ -0,0 +1,352 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapsearch.h" +#include "ldapdn.h" +#include "ldapdefs.h" + +#include +#include + +#include "ldap_debug.h" +#include +using namespace KLDAP; + +//blocking the GUI for xxx milliseconds +#define LDAPSEARCH_BLOCKING_TIMEOUT 10 + +class LdapSearchPrivate +{ +public: + LdapSearchPrivate(LdapSearch *parent) + : mParent(parent) + { + } + + void result(); + bool connect(); + void closeConnection(); + bool startSearch(const LdapDN &base, LdapUrl::Scope scope, + const QString &filter, const QStringList &attributes, + int pagesize, int count); + + LdapSearch *mParent; + LdapConnection *mConn; + LdapOperation mOp; + bool mOwnConnection, mAbandoned; + int mId, mPageSize; + LdapDN mBase; + QString mFilter; + QStringList mAttributes; + LdapUrl::Scope mScope; + + QString mErrorString; + int mError; + int mCount, mMaxCount; + bool mFinished; +}; + +void LdapSearchPrivate::result() +{ + if (mAbandoned) { + mOp.abandon(mId); + return; + } + int res = mOp.waitForResult(mId, LDAPSEARCH_BLOCKING_TIMEOUT); + + qCDebug(LDAP_LOG) << "LDAP result:" << res; + + if (res != 0 && + (res == -1 || + (mConn->ldapErrorCode() != KLDAP_SUCCESS && + mConn->ldapErrorCode() != KLDAP_SASL_BIND_IN_PROGRESS))) { + //error happened, but no timeout + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + emit mParent->result(mParent); + return; + } + + //binding + if (res == LdapOperation::RES_BIND) { + + QByteArray servercc; + servercc = mOp.serverCred(); + + qCDebug(LDAP_LOG) << "LdapSearch RES_BIND"; + if (mConn->ldapErrorCode() == KLDAP_SUCCESS) { //bind succeeded + qCDebug(LDAP_LOG) << "bind succeeded"; + LdapControls savedctrls = mOp.serverControls(); + if (mPageSize) { + LdapControls ctrls = savedctrls; + LdapControl::insert(ctrls, LdapControl::createPageControl(mPageSize)); + mOp.setServerControls(ctrls); + } + + mId = mOp.search(mBase, mScope, mFilter, mAttributes); + mOp.setServerControls(savedctrls); + } else { //next bind step + qCDebug(LDAP_LOG) << "bind next step"; + mId = mOp.bind(servercc); + } + if (mId < 0) { + if (mId == KLDAP_SASL_ERROR) { + mError = mId; + mErrorString = mConn->saslErrorString(); + } else { + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + } + emit mParent->result(mParent); + return; + } + QTimer::singleShot(0, mParent, SLOT(result())); + return; + } + + //End of entries + if (res == LdapOperation::RES_SEARCH_RESULT) { + if (mPageSize) { + QByteArray cookie; + int estsize = -1; + const int numberOfControls(mOp.controls().count()); + for (int i = 0; i < numberOfControls; ++i) { + estsize = mOp.controls().at(i).parsePageControl(cookie); + if (estsize != -1) { + break; + } + } + qCDebug(LDAP_LOG) << " estimated size:" << estsize; + if (estsize != -1 && !cookie.isEmpty()) { + LdapControls ctrls, savedctrls; + savedctrls = mOp.serverControls(); + ctrls = savedctrls; + LdapControl::insert(ctrls, LdapControl::createPageControl(mPageSize, cookie)); + mOp.setServerControls(ctrls); + mId = mOp.search(mBase, mScope, mFilter, mAttributes); + mOp.setServerControls(savedctrls); + if (mId == -1) { + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + emit mParent->result(mParent); + return; + } + //continue with the next page + QTimer::singleShot(0, mParent, SLOT(result())); + return; + } + } + mFinished = true; + emit mParent->result(mParent); + return; + } + + //Found an entry + if (res == LdapOperation::RES_SEARCH_ENTRY) { + emit mParent->data(mParent, mOp.object()); + mCount++; + } + + //If not reached the requested entries, continue + if (mMaxCount <= 0 || mCount < mMaxCount) { + QTimer::singleShot(0, mParent, SLOT(result())); + } + //If reached the requested entries, indicate it + if (mMaxCount > 0 && mCount == mMaxCount) { + qCDebug(LDAP_LOG) << mCount << " entries reached"; + emit mParent->result(mParent); + } +} + +bool LdapSearchPrivate::connect() +{ + int ret = mConn->connect(); + if (ret != KLDAP_SUCCESS) { + mError = ret; + mErrorString = mConn->connectionError(); + closeConnection(); + return false; + } + return true; +} + +void LdapSearchPrivate::closeConnection() +{ + if (mOwnConnection && mConn) { + delete mConn; + mConn = Q_NULLPTR; + } +} + +//This starts the real job +bool LdapSearchPrivate::startSearch(const LdapDN &base, LdapUrl::Scope scope, + const QString &filter, + const QStringList &attributes, int pagesize, int count) +{ + qCDebug(LDAP_LOG) << "search: base=" << base.toString() << "scope=" << (int)scope + << "filter=" << filter << "attributes=" << attributes + << "pagesize=" << pagesize; + mAbandoned = false; + mError = 0; + mErrorString.clear(); + mOp.setConnection(*mConn); + mPageSize = pagesize; + mBase = base; + mScope = scope; + mFilter = filter; + mAttributes = attributes; + mMaxCount = count; + mCount = 0; + mFinished = false; + + LdapControls savedctrls = mOp.serverControls(); + if (pagesize) { + LdapControls ctrls = savedctrls; + mConn->setOption(0x0008, Q_NULLPTR); // Disable referals or paging won't work + LdapControl::insert(ctrls, LdapControl::createPageControl(pagesize)); + mOp.setServerControls(ctrls); + } + + mId = mOp.bind(); + if (mId < 0) { + if (mId == KLDAP_SASL_ERROR) { + mError = mId; + mErrorString = mConn->saslErrorString(); + } else { + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + if (mError == -1 && mErrorString.isEmpty()) { + mErrorString = i18n("Cannot access to server. Please reconfigure it."); + } + } + return false; + } + qCDebug(LDAP_LOG) << "startSearch msg id=" << mId; + + //maybe do this with threads?- need thread-safe client libs!!! + QTimer::singleShot(0, mParent, SLOT(result())); + + return true; +} + +/////////////////////////////////////////////// + +LdapSearch::LdapSearch() + : d(new LdapSearchPrivate(this)) +{ + d->mOwnConnection = true; + d->mConn = Q_NULLPTR; +} + +LdapSearch::LdapSearch(LdapConnection &connection) + : d(new LdapSearchPrivate(this)) +{ + d->mOwnConnection = false; + d->mConn = &connection; +} + +LdapSearch::~LdapSearch() +{ + d->closeConnection(); + delete d; +} + +void LdapSearch::setConnection(LdapConnection &connection) +{ + d->closeConnection(); + d->mOwnConnection = false; + d->mConn = &connection; +} + +void LdapSearch::setClientControls(const LdapControls &ctrls) +{ + d->mOp.setClientControls(ctrls); +} + +void LdapSearch::setServerControls(const LdapControls &ctrls) +{ + d->mOp.setServerControls(ctrls); +} + +bool LdapSearch::search(const LdapServer &server, + const QStringList &attributes, int count) +{ + if (d->mOwnConnection) { + d->closeConnection(); + d->mConn = new LdapConnection(server); + if (!d->connect()) { + return false; + } + } + return d->startSearch(server.baseDn(), server.scope(), server.filter(), + attributes, server.pageSize(), count); +} + +bool LdapSearch::search(const LdapUrl &url, int count) +{ + if (d->mOwnConnection) { + d->closeConnection(); + d->mConn = new LdapConnection(url); + if (!d->connect()) { + return false; + } + } + bool critical = true; + int pagesize = url.extension(QStringLiteral("x-pagesize"), critical).toInt(); + return d->startSearch(url.dn(), url.scope(), url.filter(), + url.attributes(), pagesize, count); +} + +bool LdapSearch::search(const LdapDN &base, LdapUrl::Scope scope, + const QString &filter, const QStringList &attributes, + int pagesize, int count) +{ + Q_ASSERT(!d->mOwnConnection); + return d->startSearch(base, scope, filter, attributes, pagesize, count); +} + +void LdapSearch::continueSearch() +{ + Q_ASSERT(!d->mFinished); + d->mCount = 0; + QTimer::singleShot(0, this, SLOT(result())); +} + +bool LdapSearch::isFinished() +{ + return d->mFinished; +} + +void LdapSearch::abandon() +{ + d->mAbandoned = true; +} + +int LdapSearch::error() const +{ + return d->mError; +} + +QString LdapSearch::errorString() const +{ + return d->mErrorString; +} + +#include "moc_ldapsearch.cpp" diff --git a/src/ldapsearch.h b/src/ldapsearch.h new file mode 100644 index 0000000..108a384 --- /dev/null +++ b/src/ldapsearch.h @@ -0,0 +1,153 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPSEARCH_H +#define KLDAP_LDAPSEARCH_H + +#include +#include +#include +#include +class LdapSearchPrivate; + +#include "kldap_export.h" + +#include "ldapconnection.h" +#include "ldapcontrol.h" +#include "ldapobject.h" +#include "ldapoperation.h" +#include "ldapserver.h" +#include "ldapurl.h" + +namespace KLDAP +{ + +/** + * @brief + * This class starts a search operation on a LDAP server and returns the + * search values via a Qt signal. + */ +class KLDAP_EXPORT LdapSearch : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs an LdapSearch object + */ + LdapSearch(); + + /** + * Constructs an LdapConnection object with the given connection. If this + * form of constructor used, then always this connection will be used + * regardless of the LDAP Url or LdapServer object passed to search(). + * @param connection the connection used to construct LdapConnection object + */ + explicit LdapSearch(LdapConnection &connection); + + virtual ~LdapSearch(); + + /** + * Sets the connection for this object to use for searches from now + * onwards, regardless of the LDAP Url or LdapServer object passed to + * search(). + */ + void setConnection(LdapConnection &connection); + + /** + * Sets the client controls which will sent with each operation. + */ + void setClientControls(const LdapControls &ctrls); + + /** + * Sets the server controls which will sent with each operation. + */ + void setServerControls(const LdapControls &ctrls); + + /** + * Starts a search operation on the LDAP server @param server, + * returning the attributes specified with @param attributes. + * @param count means how many entries to list. If it's >0, then result() + * will be emitted when the number of entries is reached, but with + * isFinished() set to false. + */ + bool search(const LdapServer &server, + const QStringList &attributes = QStringList(), int count = 0); + + /** + * Starts a search operation on the given LDAP URL. + */ + bool search(const LdapUrl &url, int count = 0); + + /** + * Starts a search operation if the LdapConnection object already set + * in the constructor. + */ + bool search(const LdapDN &base, + LdapUrl::Scope scope = LdapUrl::Sub, + const QString &filter = QString(), + const QStringList &attributes = QStringList(), + int pagesize = 0, int count = 0); + + /** + * Continues the search (if you set count to non-zero in search(), and isFinished() is false) + */ + void continueSearch(); + /** + * Returns true if the search is finished else returns false. + */ + bool isFinished(); + /** + * Tries to abandon the search. + */ + void abandon(); + + /** + * Returns the error code of the search operation (0 if no error). + */ + int error() const; + + /** + * Returns the error description of the search operation. + */ + QString errorString() const; + +Q_SIGNALS: + /** + * Emitted for each result object. + */ + void data(KLDAP::LdapSearch *search, const KLDAP::LdapObject &obj); + + /** + * Emitted when the searching finished. + */ + void result(KLDAP::LdapSearch *search); + +private: + LdapSearchPrivate *const d; + + Q_PRIVATE_SLOT(d, void result()) + + Q_DISABLE_COPY(LdapSearch) +}; + +} + +#endif diff --git a/src/ldapserver.cpp b/src/ldapserver.cpp new file mode 100644 index 0000000..5d9764d --- /dev/null +++ b/src/ldapserver.cpp @@ -0,0 +1,396 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapserver.h" + +#include "ldap_debug.h" + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapServer::LdapServerPrivate +{ +public: + QString mHost; + int mPort; + LdapDN mBaseDn; + QString mUser; + QString mBindDn; + QString mRealm; + QString mPassword; + QString mMech; + QString mFilter; + int mTimeLimit; + int mSizeLimit; + int mVersion; + int mPageSize; + int mTimeout; + Security mSecurity; + Auth mAuth; + LdapUrl::Scope mScope; +}; + +LdapServer::LdapServer() + : d(new LdapServerPrivate) +{ + clear(); +} + +LdapServer::LdapServer(const LdapUrl &url) + : d(new LdapServerPrivate) +{ + clear(); + + setUrl(url); +} + +LdapServer::LdapServer(const LdapServer &that) + : d(new LdapServerPrivate) +{ + *d = *that.d; +} + +LdapServer &LdapServer::operator= (const LdapServer &that) +{ + if (this == &that) { + return *this; + } + + *d = *that.d; + + return *this; +} + +LdapServer::~LdapServer() +{ + delete d; +} + +void LdapServer::clear() +{ + d->mPort = 389; + d->mHost.clear(); + d->mUser.clear(); + d->mBindDn.clear(); + d->mMech.clear(); + d->mPassword.clear(); + d->mSecurity = None; + d->mAuth = Anonymous; + d->mVersion = 3; + d->mTimeout = 0; + d->mSizeLimit = d->mTimeLimit = d->mPageSize = 0; +} + +QString LdapServer::host() const +{ + return d->mHost; +} + +int LdapServer::port() const +{ + return d->mPort; +} + +LdapDN LdapServer::baseDn() const +{ + return d->mBaseDn; +} + +QString LdapServer::user() const +{ + return d->mUser; +} + +QString LdapServer::bindDn() const +{ + return d->mBindDn; +} + +QString LdapServer::realm() const +{ + return d->mRealm; +} + +QString LdapServer::password() const +{ + return d->mPassword; +} + +QString LdapServer::filter() const +{ + return d->mFilter; +} + +LdapUrl::Scope LdapServer::scope() const +{ + return d->mScope; +} + +int LdapServer::timeLimit() const +{ + return d->mTimeLimit; +} + +int LdapServer::sizeLimit() const +{ + return d->mSizeLimit; +} + +int LdapServer::pageSize() const +{ + return d->mPageSize; +} + +int LdapServer::version() const +{ + return d->mVersion; +} + +LdapServer::Security LdapServer::security() const +{ + return d->mSecurity; +} + +LdapServer::Auth LdapServer::auth() const +{ + return d->mAuth; +} + +QString LdapServer::mech() const +{ + return d->mMech; +} + +int LdapServer::timeout() const +{ + return d->mTimeout; +} + +void LdapServer::setHost(const QString &host) +{ + d->mHost = host; +} + +void LdapServer::setPort(int port) +{ + d->mPort = port; +} + +void LdapServer::setBaseDn(const LdapDN &baseDn) +{ + d->mBaseDn = baseDn; +} + +void LdapServer::setUser(const QString &user) +{ + d->mUser = user; +} + +void LdapServer::setBindDn(const QString &bindDn) +{ + d->mBindDn = bindDn; +} + +void LdapServer::setRealm(const QString &realm) +{ + d->mRealm = realm; +} + +void LdapServer::setPassword(const QString &password) +{ + d->mPassword = password; +} + +void LdapServer::setTimeLimit(int timelimit) +{ + d->mTimeLimit = timelimit; +} + +void LdapServer::setSizeLimit(int sizelimit) +{ + d->mSizeLimit = sizelimit; +} + +void LdapServer::setPageSize(int pagesize) +{ + d->mPageSize = pagesize; +} + +void LdapServer::setFilter(const QString &filter) +{ + d->mFilter = filter; +} + +void LdapServer::setScope(LdapUrl::Scope scope) +{ + d->mScope = scope; +} + +void LdapServer::setVersion(int version) +{ + d->mVersion = version; +} + +void LdapServer::setSecurity(Security security) +{ + d->mSecurity = security; +} + +void LdapServer::setAuth(Auth auth) +{ + d->mAuth = auth; +} + +void LdapServer::setMech(const QString &mech) +{ + d->mMech = mech; +} + +void LdapServer::setTimeout(int timeout) +{ + d->mTimeout = timeout; +} + +void LdapServer::setUrl(const LdapUrl &url) +{ + bool critical = true; + + d->mHost = url.host(); + int port = url.port(); + if (port <= 0) { + d->mPort = 389; + } else { + d->mPort = port; + } + d->mBaseDn = url.dn(); + d->mScope = url.scope(); + + d->mFilter = url.filter(); + + d->mSecurity = None; + if (url.scheme() == QLatin1String("ldaps")) { + d->mSecurity = SSL; + } else if (url.hasExtension(QStringLiteral("x-tls"))) { + d->mSecurity = TLS; + } + qCDebug(LDAP_LOG) << "security:" << d->mSecurity; + + d->mMech.clear(); + d->mUser.clear(); + d->mBindDn.clear(); + if (url.hasExtension(QStringLiteral("x-sasl"))) { + d->mAuth = SASL; + if (url.hasExtension(QStringLiteral("x-mech"))) { + d->mMech = url.extension(QStringLiteral("x-mech"), critical); + } + if (url.hasExtension(QStringLiteral("x-realm"))) { + d->mRealm = url.extension(QStringLiteral("x-realm"), critical); + } + if (url.hasExtension(QStringLiteral("bindname"))) { + d->mBindDn = url.extension(QStringLiteral("bindname"), critical); + } + d->mUser = url.userName(); + } else if (url.hasExtension(QStringLiteral("bindname"))) { + d->mAuth = Simple; + d->mBindDn = url.extension(QStringLiteral("bindname"), critical); + } else { + QString user = url.userName(); + if (user.isEmpty()) { + d->mAuth = Anonymous; + } else { + d->mAuth = Simple; + d->mBindDn = user; + } + } + d->mPassword = url.password(); + if (url.hasExtension(QStringLiteral("x-version"))) { + d->mVersion = url.extension(QStringLiteral("x-version"), critical).toInt(); + } else { + d->mVersion = 3; + } + + if (url.hasExtension(QStringLiteral("x-timeout"))) { + d->mTimeout = url.extension(QStringLiteral("x-timeout"), critical).toInt(); + } else { + d->mTimeout = 0; + } + + if (url.hasExtension(QStringLiteral("x-timelimit"))) { + d->mTimeLimit = url.extension(QStringLiteral("x-timelimit"), critical).toInt(); + } else { + d->mTimeLimit = 0; + } + + if (url.hasExtension(QStringLiteral("x-sizelimit"))) { + d->mSizeLimit = url.extension(QStringLiteral("x-sizelimit"), critical).toInt(); + } else { + d->mSizeLimit = 0; + } + + if (url.hasExtension(QStringLiteral("x-pagesize"))) { + d->mPageSize = url.extension(QStringLiteral("x-pagesize"), critical).toInt(); + } else { + d->mPageSize = 0; + } +} + +LdapUrl LdapServer::url() const +{ + LdapUrl url; + url.setScheme(d->mSecurity == SSL ? QStringLiteral("ldaps") : QStringLiteral("ldap")); + url.setPort(d->mPort); + url.setHost(d->mHost); + url.setDn(d->mBaseDn); + url.setFilter(d->mFilter); + url.setScope(d->mScope); + if (d->mAuth == SASL) { + url.setUserName(d->mUser); + url.setPassword(d->mPassword); + url.setExtension(QStringLiteral("bindname"), d->mBindDn, true); + url.setExtension(QStringLiteral("x-sasl"), QString()); + if (!d->mMech.isEmpty()) { + url.setExtension(QStringLiteral("x-mech"), d->mMech); + } + if (!d->mRealm.isEmpty()) { + url.setExtension(QStringLiteral("x-realm"), d->mRealm); + } + } else if (d->mAuth == Simple) { + url.setUserName(d->mBindDn); + url.setPassword(d->mPassword); + } + if (d->mVersion == 2) { + url.setExtension(QStringLiteral("x-version"), d->mVersion); + } + if (d->mTimeout) { + url.setExtension(QStringLiteral("x-timeout"), d->mTimeout); + } + if (d->mTimeLimit != 0) { + url.setExtension(QStringLiteral("x-timelimit"), d->mTimeLimit); + } + if (d->mSizeLimit != 0) { + url.setExtension(QStringLiteral("x-sizelimit"), d->mSizeLimit); + } + if (d->mPageSize != 0) { + url.setExtension(QStringLiteral("x-pagesize"), d->mPageSize); + } + if (d->mSecurity == TLS) { + url.setExtension(QStringLiteral("x-tls"), 1, true); + } + return url; +} diff --git a/src/ldapserver.h b/src/ldapserver.h new file mode 100644 index 0000000..7e75649 --- /dev/null +++ b/src/ldapserver.h @@ -0,0 +1,294 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPSERVER_H +#define KLDAP_LDAPSERVER_H + +#include + +#include "ldapurl.h" +#include "ldapdn.h" +#include "kldap_export.h" + +namespace KLDAP +{ + +/** + * @short A class that contains LDAP server connection settings. + * + * This class holds various parameters that are needed to connect + * to an LDAP server. + */ +class KLDAP_EXPORT LdapServer +{ +public: + /** + * Creates an empty LDAP server object. + */ + LdapServer(); + + /** + * Creates a new LDAP server object. + * + * @param url The LDAP url of the server. + */ + LdapServer(const LdapUrl &url); + + /** + * Creates a new LDAP server object from an @p other object. + */ + LdapServer(const LdapServer &other); + + /** + * Overwrites the values of the LDAP server object with + * the values from an @p other object. + */ + LdapServer &operator=(const LdapServer &other); + + /** + * Destroys the LDAP server object. + */ + virtual ~LdapServer(); + + /** + * Describes the encryption settings that can be used + * for the LDAP connection. + */ + typedef enum { + None, ///< Do not use any encryption. + TLS, ///< Use TLS encryption. + SSL ///< Use SSL encryption. + } Security; + + /** + * Describes the authentication method that can be used + * for the LDAP connection. + */ + typedef enum { + Anonymous, ///< Do no authentication. + Simple, ///< Authenticate via login and password. + SASL ///< Azthenticate with the SASL framework. + } Auth; + + /** + * Clears all server settings. + */ + void clear(); + + /** + * Sets the host of the LDAP connection. + */ + void setHost(const QString &host); + + /** + * Returns the host of the LDAP connection. + */ + QString host() const; + + /** + * Sets the port of the LDAP connection. + * If not port is set, 389 is used as default. + * @param port the LDAP port connection to set + */ + void setPort(int port); + + /** + * Returns the port of the LDAP connection. + */ + int port() const; + + /** + * Sets the @p baseDn of the LDAP connection. + */ + void setBaseDn(const LdapDN &baseDn); + + /** + * Returns the baseDn of the LDAP connection. + */ + LdapDN baseDn() const; + + /** + * Sets the @p user of the LDAP connection. + */ + void setUser(const QString &user); + + /** + * Returns the user of the LDAP connection. + */ + QString user() const; + + /** + * Sets the @p bindDn of the LDAP connection. + */ + void setBindDn(const QString &bindDn); + + /** + * Returns the bindDn of the LDAP connection. + */ + QString bindDn() const; + + /** + * Sets the @p realm of the LDAP connection. + */ + void setRealm(const QString &realm); + + /** + * Returns the realm of the LDAP connection. + */ + QString realm() const; + + /** + * Sets the @p password of the LDAP connection. + */ + void setPassword(const QString &password); + + /** + * Returns the password of the LDAP connection. + */ + QString password() const; + + /** + * Sets the protocol @p version of the LDAP connection. + * If no version is set, 3 is used as default. + * @param version the protocol version to set + */ + void setVersion(int version); + + /** + * Returns the protocol version of the LDAP connection. + */ + int version() const; + + /** + * Sets the security @p mode of the LDAP connection. + * If no security is set, None is used as default. + * @param mode the security mode to set + */ + void setSecurity(Security mode); + + /** + * Returns the security mode of the LDAP connection. + */ + Security security() const; + + /** + * Sets the @p authentication method of the LDAP connection. + * If no authentication method is set, Anonymous is used as default. + * @param authentication the authentication method to set + */ + void setAuth(Auth authentication); + + /** + * Returns the authentication method of the LDAP connection. + */ + Auth auth() const; + + /** + * Sets the @p mech of the LDAP connection. + */ + void setMech(const QString &mech); + + /** + * Returns the mech of the LDAP connection. + */ + QString mech() const; + + /** + * Sets the @p timeout of the LDAP connection. + */ + void setTimeout(int timeout); + + /** + * Returns the timeout of the LDAP connection. + */ + int timeout() const; + + /** + * Sets the search @p scope of the LDAP connection. + */ + void setScope(LdapUrl::Scope scope); + + /** + * Returns the search scope of the LDAP connection. + */ + LdapUrl::Scope scope() const; + + /** + * Sets the time @p limit of the LDAP connection. + */ + void setTimeLimit(int limit); + + /** + * Returns the time limit of the LDAP connection. + */ + int timeLimit() const; + + /** + * Sets the size @p limit of the LDAP connection. + */ + void setSizeLimit(int sizelimit); + + /** + * Returns the size limit of the LDAP connection. + */ + int sizeLimit() const; + + /** + * Sets the page @p size of the LDAP connection. + */ + void setPageSize(int size); + + /** + * Returns the page size of the LDAP connection. + */ + int pageSize() const; + + /** + * Sets the @p filter string of the LDAP connection. + */ + void setFilter(const QString &filter); + + /** + * Returns the filter string of the LDAP connection. + */ + QString filter() const; + + /** + * Sets the server parameters from an RFC2255 compliant LDAP @p url. + */ + void setUrl(const LdapUrl &url); + + /** + * Returns the server parameters as an RFC2255 compliant LDAP Url. + * The URL extensions which are supported: + * Standard: bindname + * KLDAP extensions: x-tls, x-version, x-sasl, x-mech, x-realm, + * x-sizelimit, x-timelimit, x-pagesize, x-timeout + */ + LdapUrl url() const; + +private: + class LdapServerPrivate; + LdapServerPrivate *const d; +}; + +} + +#endif diff --git a/src/ldapstructureproxymodel.cpp b/src/ldapstructureproxymodel.cpp new file mode 100644 index 0000000..6a3c485 --- /dev/null +++ b/src/ldapstructureproxymodel.cpp @@ -0,0 +1,170 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapstructureproxymodel.h" +#include "ldapmodel.h" +#include "ldapmodelnode_p.h" + +#include "ldap_debug.h" +#include + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapStructureProxyModel::LdapStructureProxyModelPrivate +{ +public: + LdapStructureProxyModelPrivate(); + +}; + +LdapStructureProxyModel::LdapStructureProxyModelPrivate::LdapStructureProxyModelPrivate() +{ + +} + +LdapStructureProxyModel::LdapStructureProxyModel(QObject *parent) + : QSortFilterProxyModel(parent), + m_d(new LdapStructureProxyModelPrivate()) +{ + +} + +LdapStructureProxyModel::~LdapStructureProxyModel() +{ + delete m_d; +} + +QVariant LdapStructureProxyModel::data(const QModelIndex &index, + int role) const +{ + // Included just in case we decide to do any special presentation of the data + // at some other point throughout the 4.x series. + return sourceModel()->data(mapToSource(index), role); +} + +bool LdapStructureProxyModel::setData(const QModelIndex &index, + const QVariant &value, + int role) +{ + Q_UNUSED(index); + Q_UNUSED(value); + Q_UNUSED(role); + return false; +} + +bool LdapStructureProxyModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const +{ + QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent); + LdapModelNode::NodeType nodeType = + static_cast( + sourceModel()->data(idx, LdapModel::NodeTypeRole).toUInt()); + return nodeType == LdapModelNode::DN; +} + +QVariant LdapStructureProxyModel::headerData(int section, + Qt::Orientation orientation, + int role) const +{ + Q_UNUSED(section); + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + return i18n("Distinguished Name"); + } + + return QVariant(); +} + +int LdapStructureProxyModel::columnCount(const QModelIndex &/*parent*/) const +{ + // No need for more than one column just to show the structure + return 1; +} + +Qt::ItemFlags LdapStructureProxyModel::flags(const QModelIndex &index) const +{ + // Included so as not to break BC in case we wish to use this later in 4.x + return sourceModel()->flags(mapToSource(index)); +} + +bool LdapStructureProxyModel::hasChildren(const QModelIndex &parent) const +{ + // We need to handle this carefully bacause of the filtering out of attributes + // and the lazy population approach. + LdapModel *model = static_cast(sourceModel()); + return model->hasChildrenOfType(mapToSource(parent), LdapModel::DistinguishedName); +} + +QModelIndex LdapStructureProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + return QSortFilterProxyModel::mapFromSource(sourceIndex); +} + +QModelIndex LdapStructureProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + return QSortFilterProxyModel::mapToSource(proxyIndex); +} + +bool LdapStructureProxyModel::insertRows(int row, int count, + const QModelIndex &parent) +{ + Q_UNUSED(row); + Q_UNUSED(count); + Q_UNUSED(parent); + return false; +} + +bool LdapStructureProxyModel::removeRows(int row, int count, + const QModelIndex &parent) +{ + Q_UNUSED(row); + Q_UNUSED(count); + Q_UNUSED(parent); + return false; +} + +void LdapStructureProxyModel::sort(int column, Qt::SortOrder order) +{ + Q_UNUSED(column); + Q_UNUSED(order); +} + +Qt::DropActions LdapStructureProxyModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QMimeData *LdapStructureProxyModel::mimeData(const QModelIndexList &indexes) const +{ + Q_UNUSED(indexes); + return Q_NULLPTR; +} + +bool LdapStructureProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + /** \todo Implement drag and drop for LdapModel */ + Q_UNUSED(data); + Q_UNUSED(action); + Q_UNUSED(row); + Q_UNUSED(column); + Q_UNUSED(parent); + return false; +} + diff --git a/src/ldapstructureproxymodel.h b/src/ldapstructureproxymodel.h new file mode 100644 index 0000000..10b0b88 --- /dev/null +++ b/src/ldapstructureproxymodel.h @@ -0,0 +1,98 @@ +/* + This file is part of libkldap. + Copyright (c) 2006 Sean Harmer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPSTRUCTUREPROXYMODEL_H +#define KLDAP_LDAPSTRUCTUREPROXYMODEL_H + +#include + +#include "kldap_export.h" + +namespace KLDAP +{ + +class KLDAP_EXPORT LdapStructureProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit LdapStructureProxyModel(QObject *parent = Q_NULLPTR); + ~LdapStructureProxyModel(); + + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::setData(). This is a placeholder for when + * LdapStructureProxyModel beomes writeable and always returns false. + */ + bool setData(const QModelIndex &index, + const QVariant &value, + int role = Qt::EditRole) Q_DECL_OVERRIDE; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + bool hasChildren(const QModelIndex &parent) const Q_DECL_OVERRIDE; + + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const Q_DECL_OVERRIDE; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const Q_DECL_OVERRIDE; + + /** + * Reimplemented from QAbstractItemModel::insertRows(). This is a placeholder for when + * LdapStructureProxyModel beomes writeable and always returns false. + */ + bool insertRows(int row, int count, + const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::removeRows(). This is a placeholder for when + * LdapStructureProxyModel beomes writeable and always returns false. + */ + bool removeRows(int row, int count, + const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::removeRows(). The default implementation + * does nothing. + */ + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) Q_DECL_OVERRIDE; + // + // Drag and drop support + // + /** + * Reimplemented from QAbstractItemModel::supportedDropActions(). The default + * implementation returns Qt::MoveAction. + */ + Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::mimedata(). This is a placeholder for when + * LdapStructureProxyModel beomes writeable and always returns 0. + */ + QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE; + /** + * Reimplemented from QAbstractItemModel::dropMimedata(). This is a placeholder for when + * LdapStructureProxyModel beomes writeable and always returns false. + */ + bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) Q_DECL_OVERRIDE; + +private: + class LdapStructureProxyModelPrivate; + LdapStructureProxyModelPrivate *const m_d; +}; + +} +#endif diff --git a/src/ldapurl.cpp b/src/ldapurl.cpp new file mode 100644 index 0000000..cc4df65 --- /dev/null +++ b/src/ldapurl.cpp @@ -0,0 +1,299 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldapurl.h" + +#include "ldap_debug.h" + +#include + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapUrl::LdapUrlPrivate +{ +public: + LdapUrlPrivate() + : m_scope(Base) + { + } + + QMap m_extensions; + QStringList m_attributes; + Scope m_scope; + QString m_filter; +}; + +LdapUrl::LdapUrl() + : d(new LdapUrlPrivate) +{ +} + +LdapUrl::LdapUrl(const QUrl &_url) + : QUrl(_url), d(new LdapUrlPrivate) +{ + QString tmp = path(); + if (tmp.startsWith(QLatin1Char('/'))) { + tmp = tmp.mid(1); + } + setPath(tmp); + parseQuery(); +} + +LdapUrl::LdapUrl(const LdapUrl &that) + : QUrl(that), d(new LdapUrlPrivate) +{ + *d = *that.d; +} + +LdapUrl &LdapUrl::operator=(const LdapUrl &that) +{ + if (this == &that) { + return *this; + } + + QUrl::operator=(that); + *d = *that.d; + + return *this; +} + +LdapUrl::~LdapUrl() +{ + delete d; +} + +void LdapUrl::setDn(const LdapDN &dn) +{ + QString tmp = dn.toString(); + if (tmp.startsWith(QLatin1Char('/'))) { + setPath(tmp); + } else { + setPath(QLatin1Char('/') + tmp); + } +} + +LdapDN LdapUrl::dn() const +{ + QString tmp = path(); + if (tmp.startsWith(QLatin1Char('/'))) { + tmp = tmp.mid(1); + } + LdapDN tmpDN(tmp); + return tmpDN; +} + +QStringList LdapUrl::attributes() const +{ + return d->m_attributes; +} + +void LdapUrl::setAttributes(const QStringList &attributes) +{ + d->m_attributes = attributes; + updateQuery(); +} + +LdapUrl::Scope LdapUrl::scope() const +{ + return d->m_scope; +} + +void LdapUrl::setScope(Scope scope) +{ + d->m_scope = scope; + updateQuery(); +} + +QString LdapUrl::filter() const +{ + return d->m_filter; +} + +void LdapUrl::setFilter(const QString &filter) +{ + d->m_filter = filter; + updateQuery(); +} + +bool LdapUrl::hasExtension(const QString &key) const +{ + return d->m_extensions.contains(key); +} + +LdapUrl::Extension LdapUrl::extension(const QString &key) const +{ + QMap::const_iterator it; + + it = d->m_extensions.constFind(key); + if (it != d->m_extensions.constEnd()) { + return (*it); + } else { + Extension ext; + ext.value = QLatin1String(""); + ext.critical = false; + return ext; + } +} + +QString LdapUrl::extension(const QString &key, bool &critical) const +{ + Extension ext; + + ext = extension(key); + critical = ext.critical; + return ext.value; +} + +void LdapUrl::setExtension(const QString &key, const LdapUrl::Extension &ext) +{ + d->m_extensions[ key ] = ext; + updateQuery(); +} + +void LdapUrl::setExtension(const QString &key, const QString &value, bool critical) +{ + Extension ext; + ext.value = value; + ext.critical = critical; + setExtension(key, ext); +} + +void LdapUrl::setExtension(const QString &key, int value, bool critical) +{ + Extension ext; + ext.value = QString::number(value); + ext.critical = critical; + setExtension(key, ext); +} + +void LdapUrl::removeExtension(const QString &key) +{ + d->m_extensions.remove(key); + updateQuery(); +} + +void LdapUrl::updateQuery() +{ + QMap::const_iterator it; + QString q(QLatin1Char('?')); + + // set the attributes to query + if (!d->m_attributes.isEmpty()) { + q += d->m_attributes.join(QStringLiteral(",")); + } + + // set the scope + q += QLatin1Char('?'); + switch (d->m_scope) { + case Sub: + q += QStringLiteral("sub"); + break; + case One: + q += QStringLiteral("one"); + break; + case Base: + q += QStringLiteral("base"); + break; + } + + // set the filter + q += QLatin1Char('?'); + if (d->m_filter != QLatin1String("(objectClass=*)") && !d->m_filter.isEmpty()) { + q += QLatin1String(toPercentEncoding(d->m_filter)); + } + + // set the extensions + q += QLatin1Char('?'); + for (it = d->m_extensions.constBegin(); it != d->m_extensions.constEnd(); ++it) { + if (it.value().critical) { + q += QLatin1Char('!'); + } + q += it.key(); + if (!it.value().value.isEmpty()) { + q += QLatin1Char('=') + QLatin1String(toPercentEncoding(it.value().value)); + } + q += QLatin1Char(','); + } + while (q.endsWith(QLatin1Char('?')) || q.endsWith(QLatin1Char(','))) { + q.remove(q.length() - 1, 1); + } + + setQuery(q); + qCDebug(LDAP_LOG) << "LDAP URL updateQuery():" << toDisplayString(); +} + +void LdapUrl::parseQuery() +{ + Extension ext; + QStringList extensions; + QString q = query(); + // remove first ? + if (q.startsWith(QLatin1Char('?'))) { + q.remove(0, 1); + } + + // split into a list + QStringList url_items = q.split(QLatin1Char('?')); + + d->m_attributes.clear(); + d->m_scope = Base; + d->m_filter = QStringLiteral("(objectClass=*)"); + d->m_extensions.clear(); + + int i = 0; + QStringList::const_iterator end(url_items.constEnd()); + for (QStringList::const_iterator it = url_items.constBegin(); + it != end; ++it, i++) { + switch (i) { + case 0: + d->m_attributes = (*it).split(QLatin1Char(','), QString::SkipEmptyParts); + break; + case 1: + if ((*it) == QLatin1String("sub")) { + d->m_scope = Sub; + } else if ((*it) == QLatin1String("one")) { + d->m_scope = One; + } + break; + case 2: + d->m_filter = fromPercentEncoding((*it).toLatin1()); + break; + case 3: + extensions = (*it).split(QLatin1Char(','), QString::SkipEmptyParts); + break; + } + } + + QString name, value; + QStringList::const_iterator end2(extensions.constEnd()); + for (QStringList::const_iterator it = extensions.constBegin(); + it != end2; ++it) { + ext.critical = false; + name = fromPercentEncoding((*it).section(QLatin1Char('='), 0, 0).toLatin1()).toLower(); + value = fromPercentEncoding((*it).section(QLatin1Char('='), 1).toLatin1()); + if (name.startsWith(QLatin1Char('!'))) { + ext.critical = true; + name.remove(0, 1); + } + qCDebug(LDAP_LOG) << "LdapUrl extensions name=" << name << "value:" << value; + ext.value = value.replace(QLatin1String("%2"), QLatin1String(",")); + setExtension(name, ext); + } +} diff --git a/src/ldapurl.h b/src/ldapurl.h new file mode 100644 index 0000000..e3a1565 --- /dev/null +++ b/src/ldapurl.h @@ -0,0 +1,185 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDAPURL_H +#define KLDAP_LDAPURL_H + +#include +#include + +#include + +#include "ldapdn.h" +#include "kldap_export.h" + +namespace KLDAP +{ + +/** + * @short A special url class for LDAP. + * + * LdapUrl implements an RFC 2255 compliant LDAP Url parser, with minimal + * differences. LDAP Urls implemented by this class has the following format: + * ldap[s]://[user[:password]@]hostname[:port]["/" [dn ["?" [attributes] + * ["?" [scope] ["?" [filter] ["?" extensions]]]]]] + */ +class KLDAP_EXPORT LdapUrl : public QUrl +{ +public: + + /** + * A class holding the extension name and state whether + * the extension is critical. + */ + typedef struct { + QString value; + bool critical; + } Extension; + + /** + * Describes the scope of the LDAP url. + */ + typedef enum { + Base, ///< Only the same level as the url. + One, ///< The level of the url and the one below. + Sub ///< All levels below the url's level. + } Scope; + + /** + * Constructs an empty LDAP url. + */ + LdapUrl(); + + /** + * Constructs a LDAP url from a KUrl @p url. + */ + explicit LdapUrl(const QUrl &url); + + /** + * Constructs a LDAP url from an other url. + */ + LdapUrl(const LdapUrl &other); + + /** + * Overwrites the values of the LDAP url with values + * from an @p other url. + */ + LdapUrl &operator=(const LdapUrl &other); + + /** + * Destroys the LDAP url. + */ + virtual ~LdapUrl(); + + /** + * Sets the @p dn part of the LDAP url. + */ + void setDn(const LdapDN &dn); + + /** + * Returns the dn part of the LDAP url. + * This is equal to path() with the slash removed from the beginning. + */ + LdapDN dn() const; + + /** + * Sets the @p attributes part of the LDAP url. + */ + void setAttributes(const QStringList &attributes); + + /** + * Returns the attributes part of the LDAP url. + */ + QStringList attributes() const; + + /** + * Sets the scope part of the LDAP url. + */ + void setScope(Scope scope); + + /** + * Returns the scope part of the LDAP url. + */ + Scope scope() const; + + /** + * Sets the filter part of the LDAP url. + */ + void setFilter(const QString &filter); + + /** + * Returns the filter part of the LDAP url. + */ + QString filter() const; + + /** + * Returns whether the specified @p extension exists in the LDAP url. + */ + bool hasExtension(const QString &extension) const; + + /** + * Returns the specified @p extension. + */ + Extension extension(const QString &extension) const; + + /** + * Returns the specified @p extension. + */ + QString extension(const QString &extension, bool &critical) const; + + /** + * Sets the specified extension @p key with the value and criticality in @p extension. + */ + void setExtension(const QString &key, const Extension &extension); + + /** + * Sets the specified extension @p key with the @p value and criticality specified. + */ + void setExtension(const QString &key, const QString &value, bool critical = false); + + /** + * Sets the specified extension @p key with the @p value and criticality specified. + */ + void setExtension(const QString &key, int value, bool critical = false); + + /** + * Removes the specified @p extension. + */ + void removeExtension(const QString &extension); + + /** + * Updates the query component from the attributes, scope, filter and extensions. + */ + void updateQuery(); + + /** + * Parses the query argument of the URL and makes it available via the + * attributes(), extension(), filter() and scope() methods + */ + void parseQuery(); + +private: + class LdapUrlPrivate; + LdapUrlPrivate *const d; +}; + +} + +#endif diff --git a/src/ldif.cpp b/src/ldif.cpp new file mode 100644 index 0000000..393ffb8 --- /dev/null +++ b/src/ldif.cpp @@ -0,0 +1,453 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ldif.h" + +#include "ldap_debug.h" + +using namespace KLDAP; + +class Q_DECL_HIDDEN Ldif::LdifPrivate +{ +public: + int mModType; + bool mDelOldRdn, mUrl; + LdapDN mDn; + QString mAttr, mNewRdn, mNewSuperior, mOid; + QByteArray mLdif, mValue; + EntryType mEntryType; + + bool mIsNewLine, mIsComment, mCritical; + ParseValue mLastParseValue; + uint mPos, mLineNumber; + QByteArray mLine; +}; + +Ldif::Ldif() : d(new LdifPrivate) +{ + startParsing(); +} + +Ldif::Ldif(const Ldif &that) : d(new LdifPrivate) +{ + *d = *that.d; + + startParsing(); +} + +Ldif &Ldif::operator=(const Ldif &that) +{ + if (this == &that) { + return *this; + } + + *d = *that.d; + + return *this; +} + +Ldif::~Ldif() +{ + delete d; +} + +QByteArray Ldif::assembleLine(const QString &fieldname, + const QByteArray &value, + uint linelen, bool url) +{ + bool safe = false; + bool isDn; + QByteArray result; + + if (url) { + result = fieldname.toUtf8() + ":< " + value; + } else { + isDn = fieldname.toLower() == QLatin1String("dn"); + //SAFE-INIT-CHAR + if (value.size() > 0 && value[0] > 0 && value[0] != '\n' && + value[0] != '\r' && value[0] != ':' && value[0] != '<') { + safe = true; + } + + //SAFE-CHAR + if (safe) { + for (int i = 1; i < value.size(); i++) { + //allow utf-8 in Distinguished Names + if ((isDn && value[i] == 0) || + (!isDn && value[i] <= 0) || + value[i] == '\r' || value[i] == '\n') { + safe = false; + break; + } + } + } + + if (value.size() == 0) { + safe = true; + } + + if (safe) { + result = fieldname.toUtf8() + ": " + value; + } else { + result = fieldname.toUtf8() + ":: " + value.toBase64(); + } + + if (linelen > 0) { + int i = (uint)(fieldname.length() + 2) > linelen ? fieldname.length() + 2 : linelen; + while (i < result.length()) { + result.insert(i, "\n "); + i += linelen + 2; + } + } + } + return result; +} + +QByteArray Ldif::assembleLine(const QString &fieldname, const QString &value, + uint linelen, bool url) +{ + return assembleLine(fieldname, value.toUtf8(), linelen, url); +} + +bool Ldif::splitLine(const QByteArray &line, QString &fieldname, QByteArray &value) +{ + int position; + QByteArray tmp; + int linelen; + +// qCDebug(LDAP_LOG) << "line:" << QString::fromUtf8(line); + + position = line.indexOf(":"); + if (position == -1) { + // strange: we did not find a fieldname + fieldname = QLatin1String(""); + value = line.trimmed(); +// qCDebug(LDAP_LOG) << "value :" << value[0]; + return false; + } + + linelen = line.size(); + fieldname = QString::fromUtf8(line.left(position).trimmed()); + + if (linelen > (position + 1) && line[ position + 1 ] == ':') { + // String is BASE64 encoded -> decode it now. + if (linelen <= (position + 3)) { + value.resize(0); + return false; + } + value = QByteArray::fromBase64(line.mid(position + 3)); + return false; + } + + if (linelen > (position + 1) && line[ position + 1 ] == '<') { + // String is an URL. + if (linelen <= (position + 3)) { + value.resize(0); + return false; + } + value = QByteArray::fromBase64(line.mid(position + 3)); + return true; + } + + if (linelen <= (position + 2)) { + value.resize(0); + return false; + } + value = line.mid(position + 2); + return false; +} + +bool Ldif::splitControl(const QByteArray &line, QString &oid, bool &critical, + QByteArray &value) +{ + QString tmp; + critical = false; + bool url = splitLine(line, tmp, value); + + qCDebug(LDAP_LOG) << "value:" << QString::fromUtf8(value); + if (tmp.isEmpty()) { + tmp = QString::fromUtf8(value); + value.resize(0); + } + if (tmp.endsWith(QLatin1String("true"))) { + critical = true; + tmp.truncate(tmp.length() - 5); + } else if (tmp.endsWith(QLatin1String("false"))) { + critical = false; + tmp.truncate(tmp.length() - 6); + } + oid = tmp; + return url; +} + +Ldif::ParseValue Ldif::processLine() +{ + + if (d->mIsComment) { + return None; + } + + ParseValue retval = None; + if (d->mLastParseValue == EndEntry) { + d->mEntryType = Entry_None; + } + + d->mUrl = splitLine(d->mLine, d->mAttr, d->mValue); + + QString attrLower = d->mAttr.toLower(); + + switch (d->mEntryType) { + case Entry_None: + if (attrLower == QLatin1String("version")) { + if (!d->mDn.isEmpty()) { + retval = Err; + } + } else if (attrLower == QLatin1String("dn")) { + qCDebug(LDAP_LOG) << "ldapentry dn:" << QString::fromUtf8(d->mValue); + d->mDn = LdapDN(QString::fromUtf8(d->mValue)); + d->mModType = Mod_None; + retval = NewEntry; + } else if (attrLower == QLatin1String("changetype")) { + if (d->mDn.isEmpty()) { + retval = Err; + } else { + QString tmpval = QString::fromUtf8(d->mValue); + qCDebug(LDAP_LOG) << "changetype:" << tmpval; + if (tmpval == QLatin1String("add")) { + d->mEntryType = Entry_Add; + } else if (tmpval == QLatin1String("delete")) { + d->mEntryType = Entry_Del; + } else if (tmpval == QLatin1String("modrdn") || tmpval == QLatin1String("moddn")) { + d->mNewRdn.clear(); + d->mNewSuperior.clear(); + d->mDelOldRdn = true; + d->mEntryType = Entry_Modrdn; + } else if (tmpval == QLatin1String("modify")) { + d->mEntryType = Entry_Mod; + } else { + retval = Err; + } + } + } else if (attrLower == QLatin1String("control")) { + d->mUrl = splitControl(d->mValue, d->mOid, d->mCritical, d->mValue); + retval = Control; + } else if (!d->mAttr.isEmpty() && d->mValue.size() > 0) { + d->mEntryType = Entry_Add; + retval = Item; + } + break; + case Entry_Add: + if (d->mAttr.isEmpty() && d->mValue.size() == 0) { + retval = EndEntry; + } else { + retval = Item; + } + break; + case Entry_Del: + if (d->mAttr.isEmpty() && d->mValue.size() == 0) { + retval = EndEntry; + } else { + retval = Err; + } + break; + case Entry_Mod: + if (d->mModType == Mod_None) { + qCDebug(LDAP_LOG) << "new modtype" << d->mAttr; + if (d->mAttr.isEmpty() && d->mValue.size() == 0) { + retval = EndEntry; + } else if (attrLower == QLatin1String("add")) { + d->mModType = Mod_Add; + } else if (attrLower == QLatin1String("replace")) { + d->mModType = Mod_Replace; + d->mAttr = QString::fromUtf8(d->mValue); + d->mValue = QByteArray(); + retval = Item; + } else if (attrLower == QLatin1String("delete")) { + d->mModType = Mod_Del; + d->mAttr = QString::fromUtf8(d->mValue); + d->mValue = QByteArray(); + retval = Item; + } else { + retval = Err; + } + } else { + if (d->mAttr.isEmpty()) { + if (QString::fromUtf8(d->mValue) == QLatin1String("-")) { + d->mModType = Mod_None; + } else if (d->mValue.size() == 0) { + retval = EndEntry; + } else { + retval = Err; + } + } else { + retval = Item; + } + } + break; + case Entry_Modrdn: + if (d->mAttr.isEmpty() && d->mValue.size() == 0) { + retval = EndEntry; + } else if (attrLower == QLatin1String("newrdn")) { + d->mNewRdn = QString::fromUtf8(d->mValue); + } else if (attrLower == QLatin1String("newsuperior")) { + d->mNewSuperior = QString::fromUtf8(d->mValue); + } else if (attrLower == QLatin1String("deleteoldrdn")) { + if (d->mValue.size() > 0 && d->mValue[0] == '0') { + d->mDelOldRdn = false; + } else if (d->mValue.size() > 0 && d->mValue[0] == '1') { + d->mDelOldRdn = true; + } else { + retval = Err; + } + } else { + retval = Err; + } + break; + } + return retval; +} + +Ldif::ParseValue Ldif::nextItem() +{ + ParseValue retval = None; + char c = 0; + + while (retval == None) { + if (d->mPos < (uint)d->mLdif.size()) { + c = d->mLdif[d->mPos]; + d->mPos++; + if (d->mIsNewLine && c == '\r') { + continue; //handle \n\r line end + } + if (d->mIsNewLine && (c == ' ' || c == '\t')) { //line folding + d->mIsNewLine = false; + continue; + } + if (d->mIsNewLine) { + d->mIsNewLine = false; + retval = processLine(); + d->mLastParseValue = retval; + d->mLine.resize(0); + d->mIsComment = (c == '#'); + } + if (c == '\n' || c == '\r') { + d->mLineNumber++; + d->mIsNewLine = true; + continue; + } + } else { + retval = MoreData; + break; + } + + if (!d->mIsComment) { + d->mLine += c; + } + } + return retval; +} + +void Ldif::endLdif() +{ + QByteArray tmp(3, '\n'); + d->mLdif = tmp; + d->mPos = 0; +} + +void Ldif::startParsing() +{ + d->mPos = d->mLineNumber = 0; + d->mDelOldRdn = false; + d->mEntryType = Entry_None; + d->mModType = Mod_None; + d->mDn = LdapDN(); + d->mNewRdn.clear(); + d->mNewSuperior.clear(); + d->mLine = QByteArray(); + d->mIsNewLine = false; + d->mIsComment = false; + d->mLastParseValue = None; +} + +void Ldif::setLdif(const QByteArray &ldif) +{ + d->mLdif = ldif; + d->mPos = 0; +} + +Ldif::EntryType Ldif::entryType() const +{ + return d->mEntryType; +} + +int Ldif::modType() const +{ + return d->mModType; +} + +LdapDN Ldif::dn() const +{ + return d->mDn; +} + +QString Ldif::newRdn() const +{ + return d->mNewRdn; +} + +QString Ldif::newSuperior() const +{ + return d->mNewSuperior; +} + +bool Ldif::delOldRdn() const +{ + return d->mDelOldRdn; +} + +QString Ldif::attr() const +{ + return d->mAttr; +} + +QByteArray Ldif::value() const +{ + return d->mValue; +} + +bool Ldif::isUrl() const +{ + return d->mUrl; +} + +bool Ldif::isCritical() const +{ + return d->mCritical; +} + +QString Ldif::oid() const +{ + return d->mOid; +} + +uint Ldif::lineNumber() const +{ + return d->mLineNumber; +} diff --git a/src/ldif.h b/src/ldif.h new file mode 100644 index 0000000..710521b --- /dev/null +++ b/src/ldif.h @@ -0,0 +1,203 @@ +/* + This file is part of libkldap. + Copyright (c) 2004-2006 Szombathelyi György + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLDAP_LDIF_H +#define KLDAP_LDIF_H + +#include +#include + +#include "ldapdn.h" +#include "kldap_export.h" + +namespace KLDAP +{ + +/** + * Ldif + * + * Ldif implements an RFC 2849 compliant Ldif parser. Ldif files are used to + * represent directory information on LDAP-based servers, or to describe a set + * of changes which are to be applied to a directory. + */ + +class KLDAP_EXPORT Ldif +{ +public: + + typedef enum { + None, NewEntry, EndEntry, Item, Control, Err, MoreData + } ParseValue; + + typedef enum { + Entry_None, Entry_Add, Entry_Del, Entry_Mod, Entry_Modrdn + } EntryType; + + typedef enum { + Mod_None, Mod_Add, Mod_Replace, Mod_Del + } ModType; + + Ldif(); + + Ldif(const Ldif &that); + Ldif &operator=(const Ldif &that); + + virtual ~Ldif(); + + /** + * Assembles fieldname and value into a valid Ldif line, BASE64 encodes the + * value if necessary and optionally splits into more lines. + * @param fieldname The name of the entry. + * @param value The value of the entry. + * @param linelen Maximum length of the lines in the result. + * @param url If true, encode value as url ( use :< ). + */ + static QByteArray assembleLine(const QString &fieldname, + const QByteArray &value, uint linelen = 0, + bool url = false); + /** + * This is the same as the above function, the only difference that + * this accepts QString as the value. + */ + static QByteArray assembleLine(const QString &fieldname, + const QString &value, uint linelen = 0, + bool url = false); + + /** + * Splits one line from an Ldif file to attribute and value components. + * @return true if value is an URL, false otherwise + */ + static bool splitLine(const QByteArray &line, QString &fieldname, + QByteArray &value); + + /** + * Splits a control specification (without the "control:" directive) + * @param line is the control directive + * @param oid will contain the OID + * @param critical will contain the criticality of control + * @param value is the control value + */ + static bool splitControl(const QByteArray &line, QString &oid, + bool &critical, QByteArray &value); + + /** + * Starts the parsing of a new Ldif + */ + void startParsing(); + + /** + * Process one Ldif line + */ + ParseValue processLine(); + + /** + * Process the Ldif until a complete item can be returned + * @return NewEntry if a new DN encountered, Item if a new item returned, + * Err if the Ldif contains error, EndEntry if the parser reached the end + * of the current entry and MoreData if the parser encountered the end of + * the current chunk of the Ldif. + * + * If you want to finish the parsing after receiving MoreData, then call + * endLdif(), so the parser can safely flush the current entry. + */ + ParseValue nextItem(); + + /** + * Sets a chunk of Ldif. Call before startParsing(), or if nextItem() + * returned MoreData. + * @param ldif the Ldif chunk to set + */ + void setLdif(const QByteArray &ldif); + + /** + * Indicates the end of the Ldif file/stream. Call if nextItem() returned + * MoreData, but actually you don't have more data. + */ + void endLdif(); + + /** + * Returns the requested LDAP operation extracted from the current entry. + */ + EntryType entryType() const; + + /** + * Returns the LDAP modify request type if entryType() returned Entry_Mod. + */ + int modType() const; + + /** + * Returns the Distinguished Name of the current entry. + */ + LdapDN dn() const; + + /** + * Returns the new Relative Distinguished Name if modType() returned + * Entry_Modrdn. + */ + QString newRdn() const; + + /** + * Returns the new parent of the entry if modType() returned Entry_Modrdn. + */ + QString newSuperior() const; + + /** + * Returns if the delete of the old RDN is required. + */ + bool delOldRdn() const; + + /** + * Returns the attribute name. + */ + QString attr() const; + + /** + * Returns the attribute value. + */ + QByteArray value() const; + + /** + * Returns if val() is an url + */ + bool isUrl() const; + + /** + * Returns the criticality level when modType() returned Control. + */ + bool isCritical() const; + + /** + * Returns the OID when modType() returned Control. + */ + QString oid() const; + + /** + * Returns the line number which the parser processes. + */ + uint lineNumber() const; + +private: + class LdifPrivate; + LdifPrivate *const d; +}; + +} + +#endif diff --git a/src/w32-ldap-help.h b/src/w32-ldap-help.h new file mode 100644 index 0000000..3f04447 --- /dev/null +++ b/src/w32-ldap-help.h @@ -0,0 +1,132 @@ +//krazy:excludeall=style +/* w32-ldap-help.h - Map utf8 based API into a wchar_t API. + + Copyright (c) 2010 Andre Heinecke + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef W32_LDAP_HELP_H +#define W32_LDAP_HELP_H + +#include +#ifdef UNICODE +# undef UNICODE +# include +# include +# define UNICODE +#else +# include +# include +#endif // UNICODE + +/* + * From the openldap manpage: + * ber_len_t is an unsigned integer of at least 32 bits used to represent + * a length. It is commonly equivalent to a size_t. ber_slen_t is the + * signed variant to ber_len_t. + */ +typedef ULONG ber_len_t; + +#ifndef timeval +#define timeval l_timeval +#endif + +/* Redirect used ldap functions to functions with win_ prefix + * to further redirect those depending on the Windows Flavour */ +//#define ldap_err2string(a) win_ldap_err2string(a) +#define ldap_init(a,b) win_ldap_init(a,b) +#define ldap_sasl_bind(a, b, c, d, e, f, g) \ + win_ldap_sasl_bind(a, b, c, d, e, f, g) +#define ldap_sasl_bind_s(a, b, c, d, e, f, g) \ + win_ldap_sasl_bind_s(a, b, c, d, e, f, g) +#define ldap_parse_sasl_bind_result ( a, b, c, d, e ) \ + win_ldap_parse_sasl_bind_result((a), (b), (c), (d), (e)) +#define ldap_get_dn(a, b) win_ldap_get_dn(a,b) +#define ldap_memfree(a) win_ldap_memfree(a) +#define ldap_mods_free(a, b) win_ldap_mods_free(a, b) +#define ldap_first_attribute(a, b, c) \ + win_ldap_first_attribute(a, b, c) +#define ldap_get_values_len(a, b, c) \ + win_ldap_get_values_len(a, b, c) +#define ldap_next_attribute(a, b, c ) \ + win_ldap_next_attribute(a, b, c) +#define ldap_parse_result(a, b, c, d, e, f, g, h) \ + win_ldap_parse_result(a, b, c, d, e, f, g, h) +#define ldap_parse_extended_result(a, b, c, d, e) \ + win_ldap_parse_extended_result(a, b, c, d, e) +#define ldap_add_ext(a, b, c, d, e, f) \ + win_ldap_add_ext((a), (b), (c), (d), (e), (f)) +#define ldap_add_ext_s(a, b, c, d, e) \ + win_ldap_add_ext_s((a), (b), (c), (d), (e)) +# define ldap_compare_ext_s(a, b, c, d, e, f) \ + win_ldap_compare_ext_s((a), (b), (c), (d), (e), (f)) +# define ldap_compare_ext(a, b, c, d, e, f, g) \ + win_ldap_compare_ext((a), (b), (c), (d), (e), (f), (g)) +# define ldap_modify_ext_s(a, b, c, d, e ) \ + win_ldap_modify_ext_s((a), (b), (c), (d), (e)) +# define ldap_search_ext(a, b, c, d, e, f, g, h, i, j, k) \ + win_ldap_search_ext((a), (b), (c), (d), (e), (f), (g), (h), (i), (j), (k)) +#define ldap_rename_ext( a, b, c, d, e, f, g, h ) \ + win_ldap_rename_ext((a), (b), (c), (d), (e), (f), (g), (h) ) +#define ldap_rename( a, b, c, d, e, f, g, h ) \ + ldap_rename_ext((a), (b), (c), (d), (e), (f), (g), (h) ) +#define ldap_delete_ext(a, b, c, d, e ) \ + win_ldap_delete_ext((a), (b), (c), (d), (e) ) +#define ldap_modify_ext(a, b, c, d, e, f ) \ + win_ldap_modify_ext( (a), (b), (c), (d), (e), (f)) +#define ldap_abandon_ext(a, b, c, d) \ + win_ldap_abandon_ext((a), (b), (c), (d)) +#define ldap_controls_free(a) win_ldap_controls_free(a) + +// Use the functions that are available on the platform +// or redirect to wrapper functions + +/* Windows offers ASCII variants of most LDAP functions + * we only have to ensure that those are used */ +# define LDAPControl LDAPControlA +# define LDAPMod LDAPModA +# define win_ldap_init(a,b) ldap_initA ((a), (b)) +# define win_ldap_simple_bind_s(a,b,c) ldap_simple_bind_sA ((a), (b), (c)) +# define win_ldap_sasl_bind(a, b, c, d, e, f, g) \ + ldap_sasl_bindA(a, b, c, d, e, f, g) +# define win_ldap_sasl_bind_s(a, b, c, d, e, f, g) \ + ldap_sasl_bind_sA(a, b, c, d, e, f, g) +# define win_ldap_search_st(a,b,c,d,e,f,g,h) \ + ldap_search_stA ((a), (b), (c), (d), (e), (f), (g), (h)) +# define win_ldap_search_ext(a, b, c, d, e, f, g, h, i, j, k) \ + my_win_ldap_search_ext((a), (b), (c), (d), (e), (f), (g), (h), (i), (j), (k)) +# define win_ldap_get_dn(a, b) ldap_get_dnA((a), (b)) +# define win_ldap_first_attribute(a,b,c) ldap_first_attributeA ((a), (b), (c)) +# define win_ldap_next_attribute(a,b,c) ldap_next_attributeA ((a), (b), (c)) +# define win_ldap_get_values_len(a,b,c) ldap_get_values_lenA ((a), (b), (c)) +# define win_ldap_memfree(a) ldap_memfreeA ((a)) +# define win_ldap_err2string(a) ldap_err2stringA((a)) +# define win_ldap_controls_free(a) ldap_controls_freeA((a)) +# define win_ldap_mods_free(a, b) ldap_mods_freeA((a), (b)) +# define win_ldap_add_ext(a, b, c, d, e, f) \ + ldap_add_extA((a), (b), (c), (d), (e), ((ulong*)f)) +# define win_ldap_add_ext_s(a, b, c, d, e) \ + ldap_add_ext_sA((a), (b), (c), (d), (e)) +# define win_ldap_parse_extended_result(a, b, c, d, e ) \ + ldap_parse_extended_resultA((*a), (b), (c), (d), (e)) +# define win_ldap_parse_result(a, b, c, d, e, f, g, h ) \ + ldap_parse_resultA((a), (b), ((ulong *)c), (d), (e), (f), (g), (h)) +# define win_ldap_modify_ext_s(a, b, c, d, e ) \ + ldap_modify_ext_sW((a), (b), (c), (d), (e)) +# define win_ldap_compare_ext_s(a, b, c, d, e, f ) \ + ldap_compare_ext_sA((a), (b), (c), (d), (e), (f)) +#endif /*W32_LDAP_HELP_H*/ -- 2.30.2