LDIF(3pm)
π LDIF(3pm) β Mozilla::LDAP::LDIF π
User Contributed Perl Documentation β¨ okay so bestie, we're doing Mozilla::LDAP::LDIF today, and it's a whole vibe.
πΈ NAME πΈ
Mozilla::LDAP::LDIF β read or write LDIF (LDAP Data Interchange Format). That's it, that's the girl. π
β¨ SYNOPSIS β¨
Import the squad:
use Mozilla::LDAP::LDIF
qw(set_Entry get_LDIF put_LDIF unpack_LDIF pack_LDIF
sort_attributes references enlist_values delist_values
read_v1 read_v0 read_file_URL_or_name);
Reading, one record at a time or a whole lil pile:
$ldif = Mozilla::LDAP::LDIF->new(*FILEHANDLE, \&read_reference, $comments);
@record = get $ldif;
@records = get $ldif ($maximum_number);
$entry = set_Entry (\entry, \@record);
$entry = readOneEntry $ldif;
@entries = readEntries $ldif ($maximum_number);
Writing, because we're putting pen to paper π :
$ldif = Mozilla::LDAP::LDIF->new(*FILEHANDLE, $options);
$success = put $ldif (@record);
$success = put $ldif (\@record, \object ...);
$success = writeOneEntry $ldif (\entry);
$success = writeEntries $ldif (\entry, \entry ...);
The functional-flavor versions:
@record = get_LDIF (*FILEHANDLE, $eof, \&read_reference, $comments);
@record = get_LDIF; # *STDIN
$success = put_LDIF (*FILEHANDLE, $options, @record);
$success = put_LDIF (*FILEHANDLE, $options, \@record, \object ...);
@record = unpack_LDIF ($string, \&read_reference, $comments);
$string = pack_LDIF ($options, @record);
$string = pack_LDIF ($options, \@record, \object ...);
Glow-ups and cleanups for your records:
@record = enlist_values (@record);
@record = delist_values (@record);
@record = sort_attributes (@record);
$DN = LDIF_get_DN (@record); # alias get_DN
@DNS = LDIF_get_DN (\@record, \object ...); # alias get_DN
$offset = next_attribute (\@record, $offset, @options);
@references = references (@record);
@references = references (\@record, \object ...);
And the file-reading besties π¦:
$success = read_v1 (\$url); # alias read_file_URL
$success = read_v0 (\$name); # alias read_file_name
$success = read_file_URL_or_name (\$url_or_name);
π REQUIRES π
The bare minimum to make her run: MIME::Base64, Exporter, Carp. Not a lot, she's low-maintenance fr.
π INSTALLATION π
Okay so listen: drop the LDIF.pm file into a subdirectory named Mozilla/LDAP, inside one of the directories listed in @INC. And site_perl is a great choice, ngl. π
π EXPORTS π
Nothing. Unless you specifically ask for it, bestie. She's not gonna come for you unprompted. π«Ά
π¦ DESCRIPTION π¦
LDIF version 1 is defined by <draft-good-ldap-ldif-03>. (Yes, that's the whole citation, we don't make the rules.)
So an LDIF record that looks like this:
DN: cn=Foo Bar, o=ITU
cn: Foo Bar
Sn: Bar
objectClass: person
objectClass: organizatio
nalPerson
jpegPhoto:< file:foobar.jpg
# comment
corresponds β in this module β to a Perl array that looks like this:
(DN => "cn=Foo Bar, o=ITU",
cn => "Foo Bar",
Sn => "Bar",
objectClass => [ "person", "organizationalPerson" ],
jpegPhoto => \"file:foobar.jpg",
'# comment', undef
)
Notice the folded organizatio / nalPerson up there? That's a continuation line being one single value, and the module un-folds it into "organizationalPerson". Slay. β¨
URLs or file names get read by a separate function β that's the whole bit. This module ships functions to read:
- a file name (that's LDIF version 0), or
- a file URL that names a local file (minimal LDIF version 1), or
- either one, if you're feeling indecisive.
And if you've got some other form of URL you wanna read? You can supply a similar function of your own. DIY queen behavior. π
π The parameter-list thing (important, bestie) π
Most output and utility methods in this module accept a parameter list that is one of two flavors:
- An LDIF array β where the first item is a string, usually
"dn". - A list of references β where each reference points to either:
- an LDIF array, or
- an object from which this module can get LDIF arrays by calling that object's
getLDIFrecordsmethod.
So how does that work? This module calls $object->getLDIFrecords(), and it's expecting that to hand back a list of references to LDIF arrays.
β οΈ Okay real talk:getLDIFrecordsmay return references to the object's own data. That's allowed. But it should not return references to anything that's gonna get modified as a side-effect of another call togetLDIFrecords()β on any object. That's the line, don't cross it. π
okay that's part one, more coming β don't go anywhere π
π METHODS π
πΈ Input πΈ
β¨ new Mozilla::LDAP::LDIF (*FILEHANDLE, \&read_reference, $comments)
Okay so this is your reader era β¨. It creates and returns an object to read LDIF from the given file. If *FILEHANDLE is not defined, you get an object that reads from *STDIN β yes, standard input, bestie.
If \&read_reference is defined, it gets called when reading each reference to another data source, with ${$_[$[]} equal to the reference. That function should copy the referent (for example, the contents of the named file) into $_[$[]. iykyk π
LDIF comment lines are ignored, unless $comments eq "comments". Periodt.
π« get $ldif
Reads an LDIF record from the given file. It combines continuation lines and base64-decodes attribute values. Returns an array of strings, representing the record. Returns a false value if end of file is encountered before an LDIF record. No cap.
π« get $ldif ($maximum_number)
Reads LDIF records from the given file until end of file is encountered or the given $maximum_number of records are read. If $maximum_number is undef (or negative), it reads until end of file. Returns an array of references to arrays, each representing one record. Returns a false value if end of file is encountered before an LDIF record, or $maximum_number is zero. Slay.
π¦ readOneEntry $ldif / readEntries $ldif ($maximum_number)
These read Mozilla::LDAP::Entry objects from the given file, and return references to them. They call Mozilla::LDAP::Conn->newEntry() to create each returned object. Return a false value if end of file is encountered before an LDIF record, or $maximum_number is zero. readOneEntry returns a reference to a single object. readEntries returns an array of references to as many as $maximum_number objects. See get (above) for more information. Bestie behavior.
π set_Entry (\entry, \@record)
Sets the DN and attributes of the given Mozilla::LDAP::Entry object from the given LDIF record. Returns a reference to the entry. Iconic.
π get_LDIF (*FILEHANDLE, $eof, \&read_reference, $comments)
Reads an LDIF record from the given file. Returns an array of strings, representing the record. Returns a false value if end of file is encountered before an LDIF record.
If *FILEHANDLE is not defined, it reads from *STDIN.
If $eof is passed, it sets it true if the end of the given file was encountered; otherwise it sets it false. This function may set $eof false and also return a record (if the record was terminated by the end of file). Lowkey wild but okay.
If \&read_reference is defined, it gets called when reading each reference to another data source, with ${$_[$[]} equal to the reference. The function should copy the referent (for example, the contents of the named file) into $_[$[].
LDIF comment lines are ignored, unless $comments eq "comments".
πΈ unpack_LDIF ($string, \&read_reference, $comments)
Reads one LDIF record from the given string. Returns an array of strings, representing the record. Returns a false value if the given string doesn't contain an LDIF record.
If \&read_reference is defined, it gets called when reading each reference to another data source, with ${$_[$[]} equal to the reference. The function should copy the referent (for example, the contents of the named file) into $_[$[].
LDIF comment lines are ignored, unless $comments eq "comments".
π read_v1 (\$url) / read_file_URL (\$url)
Changes the parameter, from a reference to a URL (string) to a string containing a copy of the contents of the file named by that URL, and returns true. Returns false if the URL doesn't name a local file, or the file can't be read.
This implements LDIF version 1, although it doesn't support URLs that refer to anything but a local file (e.g. HTTP or FTP URLs). No HTTP/FTP, bestie.
π read_v0 (\$name) / read_file_name (\$name)
Changes the parameter, from a reference to a file name to a string containing a copy of the contents of that file, and returns true. Returns false if the file can't be read.
This implements LDIF version 0.
β read_file_URL_or_name (\$url_or_name)
Changes the parameter, from a reference to a URL or file name, to a string containing a copy of the contents of the file it names, and returns true. Returns false if the file can't be read.
π Output π
π
Mozilla::LDAP::LDIF->new(*FILEHANDLE, $options)
Creates and returns an object used to write LDIF to the given file. $options are described below. Writer era activated.
β¨ put $ldif (@record) / put $ldif (\@record, \object ...) / put_LDIF (*FILEHANDLE, $options, @record) / put_LDIF (*FILEHANDLE, $options, \@record, \object ...)
Writes LDIF records to the given file. $options are described below.
π¦ writeOneEntry $ldif (\entry) / writeEntries $ldif (\entry, \entry ...)
Writes Mozilla::LDAP::Entry objects to the given file.
π pack_LDIF ($options, @record) / pack_LDIF ($options, \@record, \object ...)
Returns an LDIF string, representing the given records.
π $options
The options parameter (above) may be either "undef", indicating all default options, or a number, which is equivalent to "[max_line =>" number"]", or a reference to an array that contains a list of options, composed from:
"max_line =>" numberβ If number > 1, break output into continuation lines, so no line is longer than number bytes (not counting the end-of-line marker). Default:0(output is not broken into continuation lines). Keep it tidy."encode =>" patternβ Base64 encode output values that match pattern. Warning: As a rule, your pattern should match any value that contains an output line separator (see the SEP option, below). If any such value is not Base64 encoded, it will be output in a form that does not represent the separator bytes in LDIF form. That is, if the output is parsed as LDIF, the resulting value will be like the original value, except the separator bytes will be removed. Default:"^[:< ]|[^ -\x7E]". For example:
pack_LDIF ([encode=>"^ |[^ -\xFD]"], @record)
returns a string in which UTF-8 strings are not encoded (unless they begin with a space or contain control characters) and lines are not continued. Such a string may be easier to view or edit than standard LDIF, although it's more prone to be garbled when sent in email or processed by software designed for ASCII. It can be parsed without loss of information (by unpack_LDIF). Fancy but risky, bestie.
"sep =>" stringβ Output string at the end of each line. Default:"\n"(the usual line separator, for output text). Line vibes.
π output_separator ()
Returns the standard LDIF line separator most similar to "\n". The output option [sep => output_separator()] is recommended, if you want to produce standard LDIF output. Do this if you want the classic look.
π Utilities π
π sort_attributes (@record) / sort_attributes (\@record, \object ...)
Returns a record equivalent to each parameter, except with the attributes sorted, primarily by attribute name (ignoring case) and secondarily by attribute value (using &cmp). If the parameter list is a single record, it returns a single record; otherwise it returns a list of references to records. Organized queen.
πΈ enlist_values (@record) / enlist_values (\@record, \object ...)
Returns a record equivalent to the parameter, except with values of the same attribute type combined into a nested array. For example,
enlist_values (givenName => "Joe", givenname => "Joey", GivenName => "Joseph")
returns
(givenName => ["Joe", "Joey", "Joseph"])
If the parameter list is a single record, it returns a single record; otherwise it returns a list of references to records.
π¦ delist_values (@record) / delist_values (\@record, \object ...)
Returns a record equivalent to the parameter, except with all values contained directly (not in a nested array). For example,
delist_values (givenName => ["Joe", "Joey", "Joseph"])
returns
(givenName => "Joe", givenName => "Joey", givenName => "Joseph")
If the parameter list is a single record, it returns a single record; otherwise it returns a list of references to records.
β references (@record) / references (\@record, \object ...)
In list context, returns a list of references to each of the references to external data sources, in the given records. In scalar context, returns the length of that list; that is, the total number of references to external data sources. Count them all.
π« LDIF_get_DN (@record) / get_DN (@record)
Returns the DN of the given record. Returns undef if the first attribute of the record isn't a DN.
π« LDIF_get_DN (\@record, \object ...) / get_DN (\@record, \object ...)
Returns the DN of each of the given records, as an array with one element for each parameter. If a given record's first attribute isn't a DN, the corresponding element of the returned array is undef.
π next_attribute (\@record, $offset, @options)
Returns the offset of an attribute type in the given record. Searches forward, starting at $offset + 1, or 0 if $offset is not defined. Returns undef if no attribute is found. The @options list is composed of zero or more of the following:
"name => "expression/"type => "expressionβ Don't return an offset unless the given expression evaluates to TRUE, with$_aliased to the attribute type name."value => "expressionβ Don't return an offset unless the given expression evaluates to TRUE, with$_aliased to one of the attribute values.
In either case, the expression may be a string, which is simply evaluated (using eval), or a reference to a subroutine, which is called with $_ as its only parameter. The value returned by eval or the subroutine is taken as the result of evaluation.
If no options are given, the offset of the next attribute is returned.
Option expressions can modify the record, since they are passed an alias to an element of the record. An option can selectively prevent the evaluation of subsequent options: options are evaluated in the order they appear in the @options list, and if an option evaluates to FALSE, subsequent options are not evaluated. Careful, bestie β side effects in the options.
π DIAGNOSTICS π
Okay bestie, this is the part where the module talks back. Every one of these has a little letter code in parens β that's the severity vibe β and you're gonna want to know them, because when something yells at you at 2am you should know whether it's a warning or a whole personality crisis.
$0 can't open %s: $!β (W) warning energy.Mozilla::LDAP::LDIF::read_file_*tried to open a file and could not, and it was probably a file named inside an LDIF attrval-spec. So likeβ¦ it went to grab the file you pointed at and the door was locked. π$0 non-LDIF line: %sβ (D) the dramatic one. Your input has a line that just does not parse as LDIF. And here's the wild part: it doesn't get thrown out. It gets carried along in place of an attribute name, with an undefined value. For example,unpack_LDIF("abc")outputs this warning, and returns("abc", undef). So it keeps the garbage and shrugs. πCan't use MIME::Base64β (F) fatal, no cap. TheMIME::Base64module isn't installed. To rectify this, get a copy of MIME::Base64 fromhttp://www.perl.com/CPAN/modules/by-module/MIME/and install it. If you have trouble, try simply puttingBase64.pmin a subdirectory namedMIME, in one of the directories named in@INC(site_perlis a good choice). You'll get a correct, but relatively slow implementation. Like, it works, it's just giving π instead of ποΈ.Useless use of %s in scalar or void contextβ (W) the "bestie what are you doing" warning. The function returns multiple records, of which all but the last will be ignored by the caller. Time and space were wasted to create them. It would probably be better to call the function in list context, or to pass it only a single record. She's not mad, she's just disappointed. π
β¨ EXAMPLES β¨
Here's the part you actually came for. Copy-paste responsibly. π«Ά
use Mozilla::LDAP::LDIF qw(read_file_URL_or_name);
$in = Mozilla::LDAP::LDIF->new(*STDIN, \&read_file_URL_or_name);
$out = Mozilla::LDAP::LDIF->new(*STDOUT, 78);
@records = get $in (undef); # read to end of file (^D)
put $out (@records);
Read from stdin with URL resolution turned on, write to stdout wrapped at 78 chars, grab everything until ^D, dump it back out. Chef's kiss. π
use Mozilla::LDAP::Conn();
$conn = Mozilla::LDAP::Conn->new(...);
while ($entry = readOneEntry $in) {
add $conn ($entry);
}
Ooh this one's the main character β slurp entries one at a time out of an LDIF and shove each one straight into a connection. Bulk import vibe. π₯
use Mozilla::LDAP::LDIF qw(get_LDIF put_LDIF
references read_v1 next_attribute sort_attributes);
while (@record = get_LDIF (*STDIN, $eof)) {
# Resolve all the file URLs:
foreach my $r (references (@record)) {
read_v1 ($$r);
}
# Capitalize all the attribute names:
for ($r = undef; defined ($r = next_attribute (\@record, $r)); ) {
$record[$r] = ucfirst $record[$r];
}
# Capitalize all the title values:
next_attribute (\@record, undef,
type => '"title" eq lc $_',
value => '$_ = ucfirst; 0');
# Sort the attributes and output the record, 78 characters per line:
put_LDIF (*STDOUT, 78, sort_attributes (@record));
last if $eof;
}
Okay so this one is the full glow-up pipeline: pull a record, resolve every file URL it references, capitalize all the attribute names, capitalize all the title values, sort the attributes, print it at 78 chars a line, and bail when $eof says we're done. It's giving β¨transformation montageβ¨ and honestly? Obsessed.
π BUGS π
Yes queen, there are bugs. She's honest about them. We stan transparency.
πΈ Output Line Separator πΈ
Output lines are separated by "\n", by default. Although this works well in many cases, it is not standard LDIF unless "\n" is "\012" or "\015\012". And it is not, on some platforms (Macintosh, for example). To get standard output, use the output option "[sep => Mozilla::LDAP::LDIF::output_separator()]". Do it. Your LDIF will thank you. π
π¦ Input Line Separator π¦
This package may fail to read standard LDIF correctly, if the input record separator is not LF. To avoid this bug, set $/ = "\012". Other values of $/ work less well: CR ($/ eq "\015") handles input separated by CR or CR LF, but not LF alone; and CR LF ($/ eq "\015\012") handles input separated by CR LF, but not LF alone.
This bug arises when handling standard LDIF received 'raw' via the Internet (via HTTP, for example). There's no problem with an input file that has been converted (as generic text) from standard Internet line separators to $/ (that is, the usual line separator for the local platform). So like, download-then-convert = fine, stream-it-raw = β οΈ beware β οΈ.
π AUTHOR π
John Kristian <kristian@netscape.com>
Thanks to Leif Hedstrom, from whose code I took ideas; and to the users who took the trouble to correct my mistakes. But I accept all blame. (Not me taking accountability though β iconic. π)
π SEE ALSO π
Mozilla::LDAP::EntryMozilla::LDAP::Conn- and of course
Perl. Obviously. The blueprint. The blueprint that everything else is a remix of. π
That's the whole page bestie, you made it to the end!! π Go forth and parse some LDIF, and remember: this was written for perl v5.40.0, dated 2025-01-03, and filed under LDIF(3pm). Slay. π
β¨π¦
π¦ where this page comes from
- ubuntu questing: libmozilla-ldap-perl 1.5.3-3.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms