ldapjs / node-ldapjs

LDAP Client and Server API for node.js
http://ldapjs.org
MIT License
1.61k stars 448 forks source link

Issue reading thumbnailPhoto from LDAP #137

Closed lmusat closed 11 years ago

lmusat commented 11 years ago

Hi,

I am using the following code to extract the thumbnail photo of a user from LDAP:

var ldap = require('ldapjs'); var fs = require('fs');

var client = ldap.createClient({ url: 'ldap://someip:389' });

var user = "someuser"

var opts = { filter: '(sAMAccountName='+user+')', scope: 'sub' };

client.search('OU=someou,DC=somedc,DC=com', opts, function(err, res) { //assert.ifError(err);

res.on('searchEntry', function(entry) { var info = entry.object; console.log('entry: ' + info.dn); console.log('entry: ' + info.sn); console.log('entry: ' + info.givenName); console.log('entry: ' + info.title); console.log('entry: ' + info.memberOf); console.log('entry: ' + info.co); console.log('entry: ' + info.manager); console.log('entry: ' + info.mail); fs.writeFile("./public/images/photos/"+user+"Thumb.jpg", info.thumbnailPhoto, function(err) { if(err) { console.log("errror writing thumbnail: "+err); } else { console.log("thumbnail was saved!"); } }); //...

As it is it fails to write a proper image as it transforms some of the bytes to something else. Can someone help? For example, the first 4 bytes get transform from: ffd8 ffe0 to efbf bdef bfbd efbf bdef bfbd

Thanks.

ghost commented 11 years ago

The problem is that the attribute values are converted from buffers to strings, and ldapjs does not know that the thumbnail is really binary. Here is a function which works similar as entry.object in the example above, but returns the original buffers instead of the strings for all attributes except for dn and controls (the code is adapted from lib/messages/search_entry.js from the getter of object)

function _convert(entry) {
    var obj = {
      dn: entry.dn.toString(),
      controls: []
    };
    entry.attributes.forEach(function (a) {
      var item = a.buffers;
      if (item && item.length) {
        if (item.length > 1) {
          obj[a.type] = item.slice();
        } else {
          obj[a.type] = item[0];
        }
      } else {
        obj[a.type] = [];
      }
    });
    entry.controls.forEach(function (element, index, array) {
      obj.controls.push(element.json);
    });
    return obj;
  }

You can use entry.object for the non-binary properties and the result of this function for the binary properties. I hope this helps.

lmusat commented 11 years ago

Thanks a lot! It worked. I changed a bit the function you provided as I saw that the SearchEntry.object getter was almost the same, only using vals instead of buffers. My version only checks for thumbnailPhoto field to be object, not string:

function getProperObject(entry) {
  var obj = {
    dn: entry.dn.toString(),
    controls: []
  };
  entry.attributes.forEach(function (a) {
    var buf = a.buffers;
    var val = a.vals;
    var item;
    if ( a.type == 'thumbnailPhoto' )
      item = buf;
    else
      item = val;
    if (item && item.length) {
      if (item.length > 1) {
        obj[a.type] = item.slice();
      } else {
        obj[a.type] = item[0];
      }
    } else {
      obj[a.type] = [];
    }
  });
  entry.controls.forEach(function (element, index, array) {
    obj.controls.push(element.json);
  });
  return obj;
}

Thanks again!

pfeilbr commented 8 years ago

Searched for a good deal of time on how to solve this before I found this which worked. Thanks!

Flos commented 8 years ago

Why is this issue closed but not resolved? I struggled with the same issue. Thanks @emueller-sage and @lmusat for a solution.

That issue should really be reopend until its resolved.

tokidoki11 commented 7 years ago

Out of Curiosity tho

how could you access _vals via buffers?

As I see, the attribute value a is this

Attribute { type: 'displayName', _vals: [ <Buffer 41 73 61 6e 67> ] } [ <Buffer 41 73 61 6e 67> ]