...

Source file src/github.com/go-ldap/ldap/v3/search.go

Documentation: github.com/go-ldap/ldap/v3

     1  package ldap
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	ber "github.com/go-asn1-ber/asn1-ber"
    14  )
    15  
    16  // scope choices
    17  const (
    18  	ScopeBaseObject   = 0
    19  	ScopeSingleLevel  = 1
    20  	ScopeWholeSubtree = 2
    21  )
    22  
    23  // ScopeMap contains human readable descriptions of scope choices
    24  var ScopeMap = map[int]string{
    25  	ScopeBaseObject:   "Base Object",
    26  	ScopeSingleLevel:  "Single Level",
    27  	ScopeWholeSubtree: "Whole Subtree",
    28  }
    29  
    30  // derefAliases
    31  const (
    32  	NeverDerefAliases   = 0
    33  	DerefInSearching    = 1
    34  	DerefFindingBaseObj = 2
    35  	DerefAlways         = 3
    36  )
    37  
    38  // DerefMap contains human readable descriptions of derefAliases choices
    39  var DerefMap = map[int]string{
    40  	NeverDerefAliases:   "NeverDerefAliases",
    41  	DerefInSearching:    "DerefInSearching",
    42  	DerefFindingBaseObj: "DerefFindingBaseObj",
    43  	DerefAlways:         "DerefAlways",
    44  }
    45  
    46  // NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs.
    47  // The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the
    48  // same input map of attributes, the output entry will contain the same order of attributes
    49  func NewEntry(dn string, attributes map[string][]string) *Entry {
    50  	var attributeNames []string
    51  	for attributeName := range attributes {
    52  		attributeNames = append(attributeNames, attributeName)
    53  	}
    54  	sort.Strings(attributeNames)
    55  
    56  	var encodedAttributes []*EntryAttribute
    57  	for _, attributeName := range attributeNames {
    58  		encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName]))
    59  	}
    60  	return &Entry{
    61  		DN:         dn,
    62  		Attributes: encodedAttributes,
    63  	}
    64  }
    65  
    66  // Entry represents a single search result entry
    67  type Entry struct {
    68  	// DN is the distinguished name of the entry
    69  	DN string
    70  	// Attributes are the returned attributes for the entry
    71  	Attributes []*EntryAttribute
    72  }
    73  
    74  // GetAttributeValues returns the values for the named attribute, or an empty list
    75  func (e *Entry) GetAttributeValues(attribute string) []string {
    76  	for _, attr := range e.Attributes {
    77  		if attr.Name == attribute {
    78  			return attr.Values
    79  		}
    80  	}
    81  	return []string{}
    82  }
    83  
    84  // GetEqualFoldAttributeValues returns the values for the named attribute, or an
    85  // empty list. Attribute matching is done with strings.EqualFold.
    86  func (e *Entry) GetEqualFoldAttributeValues(attribute string) []string {
    87  	for _, attr := range e.Attributes {
    88  		if strings.EqualFold(attribute, attr.Name) {
    89  			return attr.Values
    90  		}
    91  	}
    92  	return []string{}
    93  }
    94  
    95  // GetRawAttributeValues returns the byte values for the named attribute, or an empty list
    96  func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
    97  	for _, attr := range e.Attributes {
    98  		if attr.Name == attribute {
    99  			return attr.ByteValues
   100  		}
   101  	}
   102  	return [][]byte{}
   103  }
   104  
   105  // GetEqualFoldRawAttributeValues returns the byte values for the named attribute, or an empty list
   106  func (e *Entry) GetEqualFoldRawAttributeValues(attribute string) [][]byte {
   107  	for _, attr := range e.Attributes {
   108  		if strings.EqualFold(attr.Name, attribute) {
   109  			return attr.ByteValues
   110  		}
   111  	}
   112  	return [][]byte{}
   113  }
   114  
   115  // GetAttributeValue returns the first value for the named attribute, or ""
   116  func (e *Entry) GetAttributeValue(attribute string) string {
   117  	values := e.GetAttributeValues(attribute)
   118  	if len(values) == 0 {
   119  		return ""
   120  	}
   121  	return values[0]
   122  }
   123  
   124  // GetEqualFoldAttributeValue returns the first value for the named attribute, or "".
   125  // Attribute comparison is done with strings.EqualFold.
   126  func (e *Entry) GetEqualFoldAttributeValue(attribute string) string {
   127  	values := e.GetEqualFoldAttributeValues(attribute)
   128  	if len(values) == 0 {
   129  		return ""
   130  	}
   131  	return values[0]
   132  }
   133  
   134  // GetRawAttributeValue returns the first value for the named attribute, or an empty slice
   135  func (e *Entry) GetRawAttributeValue(attribute string) []byte {
   136  	values := e.GetRawAttributeValues(attribute)
   137  	if len(values) == 0 {
   138  		return []byte{}
   139  	}
   140  	return values[0]
   141  }
   142  
   143  // GetEqualFoldRawAttributeValue returns the first value for the named attribute, or an empty slice
   144  func (e *Entry) GetEqualFoldRawAttributeValue(attribute string) []byte {
   145  	values := e.GetEqualFoldRawAttributeValues(attribute)
   146  	if len(values) == 0 {
   147  		return []byte{}
   148  	}
   149  	return values[0]
   150  }
   151  
   152  // Print outputs a human-readable description
   153  func (e *Entry) Print() {
   154  	fmt.Printf("DN: %s\n", e.DN)
   155  	for _, attr := range e.Attributes {
   156  		attr.Print()
   157  	}
   158  }
   159  
   160  // PrettyPrint outputs a human-readable description indenting
   161  func (e *Entry) PrettyPrint(indent int) {
   162  	fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN)
   163  	for _, attr := range e.Attributes {
   164  		attr.PrettyPrint(indent + 2)
   165  	}
   166  }
   167  
   168  // Describe the tag to use for struct field tags
   169  const decoderTagName = "ldap"
   170  
   171  // readTag will read the reflect.StructField value for
   172  // the key defined in decoderTagName. If omitempty is
   173  // specified, the field may not be filled.
   174  func readTag(f reflect.StructField) (string, bool) {
   175  	val, ok := f.Tag.Lookup(decoderTagName)
   176  	if !ok {
   177  		return f.Name, false
   178  	}
   179  	opts := strings.Split(val, ",")
   180  	omit := false
   181  	if len(opts) == 2 {
   182  		omit = opts[1] == "omitempty"
   183  	}
   184  	return opts[0], omit
   185  }
   186  
   187  // Unmarshal parses the Entry in the value pointed to by i
   188  //
   189  // Currently, this methods only supports struct fields of type
   190  // string, []string, int, int64, []byte, *DN, []*DN or time.Time. Other field types
   191  // will not be regarded. If the field type is a string or int but multiple
   192  // attribute values are returned, the first value will be used to fill the field.
   193  //
   194  // Example:
   195  //
   196  //	type UserEntry struct {
   197  //		// Fields with the tag key `dn` are automatically filled with the
   198  //		// objects distinguishedName. This can be used multiple times.
   199  //		DN string `ldap:"dn"`
   200  //
   201  //		// This field will be filled with the attribute value for
   202  //		// userPrincipalName. An attribute can be read into a struct field
   203  //		// multiple times. Missing attributes will not result in an error.
   204  //		UserPrincipalName string `ldap:"userPrincipalName"`
   205  //
   206  //		// memberOf may have multiple values. If you don't
   207  //		// know the amount of attribute values at runtime, use a string array.
   208  //		MemberOf []string `ldap:"memberOf"`
   209  //
   210  //		// ID is an integer value, it will fail unmarshaling when the given
   211  //		// attribute value cannot be parsed into an integer.
   212  //		ID int `ldap:"id"`
   213  //
   214  //		// LongID is similar to ID but uses an int64 instead.
   215  //		LongID int64 `ldap:"longId"`
   216  //
   217  //		// Data is similar to MemberOf a slice containing all attribute
   218  //		// values.
   219  //		Data []byte `ldap:"data"`
   220  //
   221  //		// Time is parsed with the generalizedTime spec into a time.Time
   222  //		Created time.Time `ldap:"createdTimestamp"`
   223  //
   224  //		// *DN is parsed with the ParseDN
   225  //		Owner *ldap.DN `ldap:"owner"`
   226  //
   227  //		// []*DN is parsed with the ParseDN
   228  //		Children []*ldap.DN `ldap:"children"`
   229  //
   230  //		// This won't work, as the field is not of type string. For this
   231  //		// to work, you'll have to temporarily store the result in string
   232  //		// (or string array) and convert it to the desired type afterwards.
   233  //		UserAccountControl uint32 `ldap:"userPrincipalName"`
   234  //	}
   235  //	user := UserEntry{}
   236  //
   237  //	if err := result.Unmarshal(&user); err != nil {
   238  //		// ...
   239  //	}
   240  func (e *Entry) Unmarshal(i interface{}) (err error) {
   241  	// Make sure it's a ptr
   242  	if vo := reflect.ValueOf(i).Kind(); vo != reflect.Ptr {
   243  		return fmt.Errorf("ldap: cannot use %s, expected pointer to a struct", vo)
   244  	}
   245  
   246  	sv, st := reflect.ValueOf(i).Elem(), reflect.TypeOf(i).Elem()
   247  	// Make sure it's pointing to a struct
   248  	if sv.Kind() != reflect.Struct {
   249  		return fmt.Errorf("ldap: expected pointer to a struct, got %s", sv.Kind())
   250  	}
   251  
   252  	for n := 0; n < st.NumField(); n++ {
   253  		// Holds struct field value and type
   254  		fv, ft := sv.Field(n), st.Field(n)
   255  
   256  		// skip unexported fields
   257  		if ft.PkgPath != "" {
   258  			continue
   259  		}
   260  
   261  		// omitempty can be safely discarded, as it's not needed when unmarshalling
   262  		fieldTag, _ := readTag(ft)
   263  
   264  		// Fill the field with the distinguishedName if the tag key is `dn`
   265  		if fieldTag == "dn" {
   266  			fv.SetString(e.DN)
   267  			continue
   268  		}
   269  
   270  		values := e.GetAttributeValues(fieldTag)
   271  		if len(values) == 0 {
   272  			continue
   273  		}
   274  
   275  		switch fv.Interface().(type) {
   276  		case []string:
   277  			for _, item := range values {
   278  				fv.Set(reflect.Append(fv, reflect.ValueOf(item)))
   279  			}
   280  		case string:
   281  			fv.SetString(values[0])
   282  		case []byte:
   283  			fv.SetBytes([]byte(values[0]))
   284  		case int, int64:
   285  			intVal, err := strconv.ParseInt(values[0], 10, 64)
   286  			if err != nil {
   287  				return fmt.Errorf("ldap: could not parse value '%s' into int field", values[0])
   288  			}
   289  			fv.SetInt(intVal)
   290  		case time.Time:
   291  			t, err := ber.ParseGeneralizedTime([]byte(values[0]))
   292  			if err != nil {
   293  				return fmt.Errorf("ldap: could not parse value '%s' into time.Time field", values[0])
   294  			}
   295  			fv.Set(reflect.ValueOf(t))
   296  		case *DN:
   297  			dn, err := ParseDN(values[0])
   298  			if err != nil {
   299  				return fmt.Errorf("ldap: could not parse value '%s' into *ldap.DN field", values[0])
   300  			}
   301  			fv.Set(reflect.ValueOf(dn))
   302  		case []*DN:
   303  			for _, item := range values {
   304  				dn, err := ParseDN(item)
   305  				if err != nil {
   306  					return fmt.Errorf("ldap: could not parse value '%s' into *ldap.DN field", item)
   307  				}
   308  				fv.Set(reflect.Append(fv, reflect.ValueOf(dn)))
   309  			}
   310  		default:
   311  			return fmt.Errorf("ldap: expected field to be of type string, []string, int, int64, []byte, *DN, []*DN or time.Time, got %v", ft.Type)
   312  		}
   313  	}
   314  	return
   315  }
   316  
   317  // NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
   318  func NewEntryAttribute(name string, values []string) *EntryAttribute {
   319  	var bytes [][]byte
   320  	for _, value := range values {
   321  		bytes = append(bytes, []byte(value))
   322  	}
   323  	return &EntryAttribute{
   324  		Name:       name,
   325  		Values:     values,
   326  		ByteValues: bytes,
   327  	}
   328  }
   329  
   330  // EntryAttribute holds a single attribute
   331  type EntryAttribute struct {
   332  	// Name is the name of the attribute
   333  	Name string
   334  	// Values contain the string values of the attribute
   335  	Values []string
   336  	// ByteValues contain the raw values of the attribute
   337  	ByteValues [][]byte
   338  }
   339  
   340  // Print outputs a human-readable description
   341  func (e *EntryAttribute) Print() {
   342  	fmt.Printf("%s: %s\n", e.Name, e.Values)
   343  }
   344  
   345  // PrettyPrint outputs a human-readable description with indenting
   346  func (e *EntryAttribute) PrettyPrint(indent int) {
   347  	fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
   348  }
   349  
   350  // SearchResult holds the server's response to a search request
   351  type SearchResult struct {
   352  	// Entries are the returned entries
   353  	Entries []*Entry
   354  	// Referrals are the returned referrals
   355  	Referrals []string
   356  	// Controls are the returned controls
   357  	Controls []Control
   358  }
   359  
   360  // Print outputs a human-readable description
   361  func (s *SearchResult) Print() {
   362  	for _, entry := range s.Entries {
   363  		entry.Print()
   364  	}
   365  }
   366  
   367  // PrettyPrint outputs a human-readable description with indenting
   368  func (s *SearchResult) PrettyPrint(indent int) {
   369  	for _, entry := range s.Entries {
   370  		entry.PrettyPrint(indent)
   371  	}
   372  }
   373  
   374  // appendTo appends all entries of `s` to `r`
   375  func (s *SearchResult) appendTo(r *SearchResult) {
   376  	r.Entries = append(r.Entries, s.Entries...)
   377  	r.Referrals = append(r.Referrals, s.Referrals...)
   378  	r.Controls = append(r.Controls, s.Controls...)
   379  }
   380  
   381  // SearchSingleResult holds the server's single entry response to a search request
   382  type SearchSingleResult struct {
   383  	// Entry is the returned entry
   384  	Entry *Entry
   385  	// Referral is the returned referral
   386  	Referral string
   387  	// Controls are the returned controls
   388  	Controls []Control
   389  	// Error is set when the search request was failed
   390  	Error error
   391  }
   392  
   393  // Print outputs a human-readable description
   394  func (s *SearchSingleResult) Print() {
   395  	s.Entry.Print()
   396  }
   397  
   398  // PrettyPrint outputs a human-readable description with indenting
   399  func (s *SearchSingleResult) PrettyPrint(indent int) {
   400  	s.Entry.PrettyPrint(indent)
   401  }
   402  
   403  // SearchRequest represents a search request to send to the server
   404  type SearchRequest struct {
   405  	BaseDN       string
   406  	Scope        int
   407  	DerefAliases int
   408  	SizeLimit    int
   409  	TimeLimit    int
   410  	TypesOnly    bool
   411  	Filter       string
   412  	Attributes   []string
   413  	Controls     []Control
   414  }
   415  
   416  func (req *SearchRequest) appendTo(envelope *ber.Packet) error {
   417  	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request")
   418  	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.BaseDN, "Base DN"))
   419  	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(req.Scope), "Scope"))
   420  	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(req.DerefAliases), "Deref Aliases"))
   421  	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(req.SizeLimit), "Size Limit"))
   422  	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(req.TimeLimit), "Time Limit"))
   423  	pkt.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, req.TypesOnly, "Types Only"))
   424  	// compile and encode filter
   425  	filterPacket, err := CompileFilter(req.Filter)
   426  	if err != nil {
   427  		return err
   428  	}
   429  	pkt.AppendChild(filterPacket)
   430  	// encode attributes
   431  	attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
   432  	for _, attribute := range req.Attributes {
   433  		attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
   434  	}
   435  	pkt.AppendChild(attributesPacket)
   436  
   437  	envelope.AppendChild(pkt)
   438  	if len(req.Controls) > 0 {
   439  		envelope.AppendChild(encodeControls(req.Controls))
   440  	}
   441  
   442  	return nil
   443  }
   444  
   445  // NewSearchRequest creates a new search request
   446  func NewSearchRequest(
   447  	BaseDN string,
   448  	Scope, DerefAliases, SizeLimit, TimeLimit int,
   449  	TypesOnly bool,
   450  	Filter string,
   451  	Attributes []string,
   452  	Controls []Control,
   453  ) *SearchRequest {
   454  	return &SearchRequest{
   455  		BaseDN:       BaseDN,
   456  		Scope:        Scope,
   457  		DerefAliases: DerefAliases,
   458  		SizeLimit:    SizeLimit,
   459  		TimeLimit:    TimeLimit,
   460  		TypesOnly:    TypesOnly,
   461  		Filter:       Filter,
   462  		Attributes:   Attributes,
   463  		Controls:     Controls,
   464  	}
   465  }
   466  
   467  // SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the
   468  // search request. All paged LDAP query responses will be buffered and the final result will be returned atomically.
   469  // The following four cases are possible given the arguments:
   470  //   - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
   471  //   - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
   472  //   - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
   473  //   - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
   474  //
   475  // A requested pagingSize of 0 is interpreted as no limit by LDAP servers.
   476  func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
   477  	var pagingControl *ControlPaging
   478  
   479  	control := FindControl(searchRequest.Controls, ControlTypePaging)
   480  	if control == nil {
   481  		pagingControl = NewControlPaging(pagingSize)
   482  		searchRequest.Controls = append(searchRequest.Controls, pagingControl)
   483  	} else {
   484  		castControl, ok := control.(*ControlPaging)
   485  		if !ok {
   486  			return nil, fmt.Errorf("expected paging control to be of type *ControlPaging, got %v", control)
   487  		}
   488  		if castControl.PagingSize != pagingSize {
   489  			return nil, fmt.Errorf("paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
   490  		}
   491  		pagingControl = castControl
   492  	}
   493  
   494  	searchResult := new(SearchResult)
   495  	for {
   496  		result, err := l.Search(searchRequest)
   497  		if result != nil {
   498  			result.appendTo(searchResult)
   499  		} else {
   500  			if err == nil {
   501  				// We have to do this beautifulness in case something absolutely strange happens, which
   502  				// should only occur in case there is no packet, but also no error.
   503  				return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
   504  			}
   505  		}
   506  		if err != nil {
   507  			// If an error occurred, all results that have been received so far will be returned
   508  			return searchResult, err
   509  		}
   510  
   511  		l.Debug.Printf("Looking for Paging Control...")
   512  		pagingResult := FindControl(result.Controls, ControlTypePaging)
   513  		if pagingResult == nil {
   514  			pagingControl = nil
   515  			l.Debug.Printf("Could not find paging control.  Breaking...")
   516  			break
   517  		}
   518  
   519  		cookie := pagingResult.(*ControlPaging).Cookie
   520  		if len(cookie) == 0 {
   521  			pagingControl = nil
   522  			l.Debug.Printf("Could not find cookie.  Breaking...")
   523  			break
   524  		}
   525  		pagingControl.SetCookie(cookie)
   526  	}
   527  
   528  	if pagingControl != nil {
   529  		l.Debug.Printf("Abandoning Paging...")
   530  		pagingControl.PagingSize = 0
   531  		if _, err := l.Search(searchRequest); err != nil {
   532  			return searchResult, err
   533  		}
   534  	}
   535  
   536  	return searchResult, nil
   537  }
   538  
   539  // Search performs the given search request
   540  func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
   541  	msgCtx, err := l.doRequest(searchRequest)
   542  	if err != nil {
   543  		return nil, err
   544  	}
   545  	defer l.finishMessage(msgCtx)
   546  
   547  	result := &SearchResult{
   548  		Entries:   make([]*Entry, 0),
   549  		Referrals: make([]string, 0),
   550  		Controls:  make([]Control, 0),
   551  	}
   552  
   553  	for {
   554  		packet, err := l.readPacket(msgCtx)
   555  		if err != nil {
   556  			return result, err
   557  		}
   558  
   559  		switch packet.Children[1].Tag {
   560  		case 4:
   561  			entry := &Entry{
   562  				DN:         packet.Children[1].Children[0].Value.(string),
   563  				Attributes: unpackAttributes(packet.Children[1].Children[1].Children),
   564  			}
   565  			result.Entries = append(result.Entries, entry)
   566  		case 5:
   567  			err := GetLDAPError(packet)
   568  			if err != nil {
   569  				return result, err
   570  			}
   571  			if len(packet.Children) == 3 {
   572  				for _, child := range packet.Children[2].Children {
   573  					decodedChild, err := DecodeControl(child)
   574  					if err != nil {
   575  						return result, fmt.Errorf("failed to decode child control: %s", err)
   576  					}
   577  					result.Controls = append(result.Controls, decodedChild)
   578  				}
   579  			}
   580  			return result, nil
   581  		case 19:
   582  			result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
   583  		}
   584  	}
   585  }
   586  
   587  // SearchAsync performs a search request and returns all search results asynchronously.
   588  // This means you get all results until an error happens (or the search successfully finished),
   589  // e.g. for size / time limited requests all are recieved until the limit is reached.
   590  // To stop the search, call cancel function of the context.
   591  func (l *Conn) SearchAsync(
   592  	ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response {
   593  	r := newSearchResponse(l, bufferSize)
   594  	r.start(ctx, searchRequest)
   595  	return r
   596  }
   597  
   598  // Syncrepl is a short name for LDAP Sync Replication engine that works on the
   599  // consumer-side. This can perform a persistent search and returns an entry
   600  // when the entry is updated on the server side.
   601  // To stop the search, call cancel function of the context.
   602  func (l *Conn) Syncrepl(
   603  	ctx context.Context, searchRequest *SearchRequest, bufferSize int,
   604  	mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
   605  ) Response {
   606  	control := NewControlSyncRequest(mode, cookie, reloadHint)
   607  	searchRequest.Controls = append(searchRequest.Controls, control)
   608  	r := newSearchResponse(l, bufferSize)
   609  	r.start(ctx, searchRequest)
   610  	return r
   611  }
   612  
   613  // unpackAttributes will extract all given LDAP attributes and it's values
   614  // from the ber.Packet
   615  func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
   616  	entries := make([]*EntryAttribute, len(children))
   617  	for i, child := range children {
   618  		length := len(child.Children[1].Children)
   619  		entry := &EntryAttribute{
   620  			Name: child.Children[0].Value.(string),
   621  			// pre-allocate the slice since we can determine
   622  			// the number of attributes at this point
   623  			Values:     make([]string, length),
   624  			ByteValues: make([][]byte, length),
   625  		}
   626  
   627  		for i, value := range child.Children[1].Children {
   628  			entry.ByteValues[i] = value.ByteValue
   629  			entry.Values[i] = value.Value.(string)
   630  		}
   631  		entries[i] = entry
   632  	}
   633  
   634  	return entries
   635  }
   636  
   637  // DirSync does a Search with dirSync Control.
   638  func (l *Conn) DirSync(
   639  	searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte,
   640  ) (*SearchResult, error) {
   641  	control := FindControl(searchRequest.Controls, ControlTypeDirSync)
   642  	if control == nil {
   643  		c := NewRequestControlDirSync(flags, maxAttrCount, cookie)
   644  		searchRequest.Controls = append(searchRequest.Controls, c)
   645  	} else {
   646  		c := control.(*ControlDirSync)
   647  		if c.Flags != flags {
   648  			return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", c.Flags, flags)
   649  		}
   650  		if c.MaxAttrCount != maxAttrCount {
   651  			return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", c.MaxAttrCount, maxAttrCount)
   652  		}
   653  	}
   654  	searchResult, err := l.Search(searchRequest)
   655  	l.Debug.Printf("Looking for result...")
   656  	if err != nil {
   657  		return nil, err
   658  	}
   659  	if searchResult == nil {
   660  		return nil, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
   661  	}
   662  
   663  	l.Debug.Printf("Looking for DirSync Control...")
   664  	resultControl := FindControl(searchResult.Controls, ControlTypeDirSync)
   665  	if resultControl == nil {
   666  		l.Debug.Printf("Could not find dirSyncControl control.  Breaking...")
   667  		return searchResult, nil
   668  	}
   669  
   670  	cookie = resultControl.(*ControlDirSync).Cookie
   671  	if len(cookie) == 0 {
   672  		l.Debug.Printf("Could not find cookie.  Breaking...")
   673  		return searchResult, nil
   674  	}
   675  
   676  	return searchResult, nil
   677  }
   678  
   679  // DirSyncDirSyncAsync performs a search request and returns all search results
   680  // asynchronously. This is efficient when the server returns lots of entries.
   681  func (l *Conn) DirSyncAsync(
   682  	ctx context.Context, searchRequest *SearchRequest, bufferSize int,
   683  	flags, maxAttrCount int64, cookie []byte,
   684  ) Response {
   685  	control := NewRequestControlDirSync(flags, maxAttrCount, cookie)
   686  	searchRequest.Controls = append(searchRequest.Controls, control)
   687  	r := newSearchResponse(l, bufferSize)
   688  	r.start(ctx, searchRequest)
   689  	return r
   690  }
   691  

View as plain text