Skip to content

Commit

Permalink
Adding Server Control to Search()
Browse files Browse the repository at this point in the history
  • Loading branch information
RockfordWei committed Jan 24, 2017
1 parent ce37b7a commit 709be0b
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 34 deletions.
78 changes: 49 additions & 29 deletions Sources/PerfectLDAP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,6 @@ public class LDAP {
case message(String)
}//end enum

public static func withCArrayOfString(array: [String] = [], operation: (UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> Void) {

if array.isEmpty {
operation(nil)
return
}//end if

// duplicate the array and append a null string
var attr: [String?] = array
attr.append(nil)

// duplicate again and turn it into an array of pointers
var parr = attr.map { $0 == nil ? nil : strdup($0!) }

// perform the operation
parr.withUnsafeMutableBufferPointer { operation($0.baseAddress) }

// release allocated string pointers.
for p in parr { free(UnsafeMutablePointer(mutating: p)) }
}//end withCArrayOfString

/// Explain the error code, typical usage is `throw Exception.message(LDAP.error(error_number))`
/// - parameters:
/// - errno: Int32, the error number return by most ldap_XXX functions
Expand Down Expand Up @@ -473,28 +452,68 @@ public class LDAP {
}//end init
}//end struct

/// constant to indicate a sorting order: ascendant or descendant.
public enum SortingOrder {
case ASC
case DSC
}//end SortingOrder

/// generate a standard sorting string from a series of fields
/// - parameters:
/// - sortedBy: an array of tuple, which tells each field to sort in what order
/// - returns:
/// the sorting language, as a string
@discardableResult
public static func sortingString( sortedBy: [(String, SortingOrder)] = [] ) -> String {
return sortedBy.reduce("") { previous, next in
let str = next.1 == .ASC ? next.0 : "-" + next.0
return previous.isEmpty ? str : previous + " " + str
}//end reduce
}//end sortingString

/// synchronized search
/// - parameters:
/// - base: String, search base domain (dn), default = ""
/// - filter: String, the filter of query, default = "(objectclass=*)", means all possible results
/// - scope: See Scope, BASE, SINGLE_LEVEL, SUBTREE or CHILDREN
/// - sortedBy: a sorting string, may be generated by LDAP.sortingString()
/// - returns:
/// ResultSet. See ResultSet
/// - throws:
/// Exception.message
@discardableResult
public func search(base:String = "", filter:String = "(objectclass=*)", scope:Scope = .BASE, attributes: [String] = []) throws -> ResultSet? {
public func search(base:String = "", filter:String = "(objectclass=*)", scope:Scope = .BASE, attributes: [String] = [], sortedBy: String = "") throws -> ResultSet? {

var serverControl = UnsafeMutablePointer<LDAPControl>(bitPattern: 0)

if !sortedBy.isEmpty {
var sortKeyList = UnsafeMutablePointer<UnsafeMutablePointer<LDAPSortKey>?>(bitPattern: 0)
let sortString = strdup(sortedBy)
var r = ldap_create_sort_keylist(&sortKeyList, sortString)
free(sortString)
if r != 0 {
throw Exception.message(LDAP.error(r))
}//end if

r = ldap_create_sort_control(self.ldap, sortKeyList, 0, &serverControl)
ldap_free_sort_keylist(sortKeyList)
if r != 0 {
throw Exception.message(LDAP.error(r))
}//end if
}//end if

// prepare the return set
var msg = OpaquePointer(bitPattern: 0)

// prepare the return value
var r = Int32(0)

LDAP.withCArrayOfString(array: attributes) { pAttribute in
let r = withCArrayOfString(array: attributes) { pAttribute -> Int32 in

// perform the search
r = ldap_search_ext_s(self.ldap, base, scope.rawValue, filter, pAttribute, 0, nil, nil, nil, 0, &msg)
let result = ldap_search_ext_s(self.ldap, base, scope.rawValue, filter, pAttribute, 0, &serverControl, nil, nil, 0, &msg)

if serverControl != nil {
ldap_control_free(serverControl)
}
return result
}//end

// validate the query
Expand All @@ -516,13 +535,14 @@ public class LDAP {
/// - base: String, search base domain (dn), default = ""
/// - filter: String, the filter of query, default = "(objectclass=*)", means all possible results
/// - scope: See Scope, BASE, SINGLE_LEVEL, SUBTREE or CHILDREN
/// - sortedBy: a sorting string, may be generated by LDAP.sortingString()
/// - completion: callback with a parameter of ResultSet, nil if failed
@discardableResult
public func search(base:String = "", filter:String = "(objectclass=*)", scope:Scope = .BASE, completion: @escaping (ResultSet?)-> Void) {
public func search(base:String = "", filter:String = "(objectclass=*)", scope:Scope = .BASE, sortedBy: String = "", completion: @escaping (ResultSet?)-> Void) {
Threading.dispatch {
var rs: ResultSet? = nil
do {
rs = try self.search(base: base, filter: filter, scope: scope)
rs = try self.search(base: base, filter: filter, scope: scope, sortedBy: sortedBy)
}catch {
rs = nil
}//end catch
Expand Down
23 changes: 22 additions & 1 deletion Sources/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ extension Array {
/// - return:
/// a pointer array with each pointer is pointing the corresponding element, ending with a null pointer.
public func asUnsafeNullTerminatedPointers() -> UnsafeMutablePointer<UnsafeMutablePointer<Element>?> {

let pArray = self.map { value -> UnsafeMutablePointer<Element>? in
let p = UnsafeMutablePointer<Element>.allocate(capacity: 1)
p.initialize(to: value)
Expand All @@ -58,3 +57,25 @@ extension Array {
return pointers
}//func
}//end array

public func withCArrayOfString<R>(array: [String] = [], _ body: (UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> R) -> R {

if array.isEmpty {
return body(nil)
}//end if

// duplicate the array and append a null string
var attr: [String?] = array
attr.append(nil)

// duplicate again and turn it into an array of pointers
var parr = attr.map { $0 == nil ? nil : strdup($0!) }

// perform the operation
let r = parr.withUnsafeMutableBufferPointer { body ($0.baseAddress) }

// release allocated string pointers.
for p in parr { free(UnsafeMutablePointer(mutating: p)) }

return r
}//end withCArrayOfString
25 changes: 21 additions & 4 deletions Tests/PerfectLDAPTests/PerfectLDAPTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class PerfectLDAPTests: XCTestCase {
XCTFail("search return nil")
return
}//end guard
print(r)
print(r.dictionary)
ser.fulfill()
}//end search

Expand All @@ -85,11 +85,27 @@ class PerfectLDAPTests: XCTestCase {
}//end guard
print("-------------------------------------------------------")
print(rs.dictionary)
print("-------------------------------------------------------")
}catch(let err) {
XCTFail("error: \(err)")
}


}

func testServerSort () {
do {
let ldap = try LDAP(url: "ldap://192.168.56.13", username: testUSR, password: testPWD, codePage: .GB2312)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
let sort = LDAP.sortingString(sortedBy: [("displayName", .DSC), ("initials", .ASC)])
print(sort)
guard let res = try ldap.search(base:"cn=users,dc=p,dc=com",scope:.SUBTREE, attributes: ["displayName", "initials"], sortedBy: sort) else {
XCTFail("server control failed")
return
}//end guard
print(res.dictionary)
}catch(let err) {
XCTFail("server control: \(err)")
}

}

func testAttributeMod () {
Expand Down Expand Up @@ -129,7 +145,8 @@ class PerfectLDAPTests: XCTestCase {
("testLoginSync", testLoginSync),
("testSearch", testSearch),
("testSearchSync", testSearchSync),
("testAttributeMod", testAttributeMod)
("testAttributeMod", testAttributeMod),
("testServerSort", testServerSort)
]
}
}

0 comments on commit 709be0b

Please sign in to comment.