# ldapvi User Manual

`ldapvi` is an interactive LDAP client for Unix terminals. Using it, you can update LDAP entries with a text editor.

Think of it as `vipw`(1) for LDAP.

ldapvi was written by David Lichteblau and is available at http://www.lichteblau.com/ldapvi/.

## Installing ldapvi

ldapvi uses autoconf and should be rather easy to install.

It used to work on Linux, Solaris, FreeBSD, IRIX, and Cygwin for me and seems to work on MacOS X and NetBSD for others. Current versions are only really tested on Linux though, so please send a bug report if a port is broken.

    $ ./configure && make
    # make install

Prerequisites.

- Recent OpenLDAP client library (>= 2.2 preferred, 2.1 tolerated)
- glib-2.0
- popt
- curses
- GNU make
- OpenSSL or GnuTLS, choose using `./configure --with-libcrypto`
- GNU readline
- Build dependency: Cyrus SASL. SASL support is disabled in ldapvi if Cyrus SASL headers cannot be found at compilation time. (Whether libldap was compiled with SASL support is only relevant at run time.)

Note that binaries compiled for libldap 2.1 will crash when ran with libldap 2.2.

The choice of GnuTLS over OpenSSL affects only the use of hashing routines for the `userPassword` convenience encodings md5, smd5, sha, and ssha. GnuTLS support is offered because, according to Debian, OpenSSL is not GPL compatible.

## Getting started

The quickest way to try ldapvi is using this:

    $ ldapvi -d --host HOSTNAME

Explanation. This will bind anonymously to `hostname`, read all entries the server has and open them in the editor.

`--host` is the one option you really need to give. It can be one of:

- A domain name.
- `ldap://hostname[:port]`
- `ldaps://hostname[:port]`

`-d` is short for `--discover` and lets ldapvi search all of the server's naming contexts. Without `-d`, you would have had to specify the base DN yourself using `--base`, and you know how inconvenient those LDAP DNs are to type.

Using a configuration file. As easy as that was, you might want to put the hostname and other search options like the precise base DN to use into a configuration file.

To do that, you might like

    $ ldapvi --config --discover --host HOSTNAME

This will ask the server for its naming contexts and print sample contents for `~/.ldaprc` (or `/etc/ldap.conf`).

(Note that ldapvi also has its own configuration file format which supports multiple configuration profiles and all of its options.)

    # ldap.conf(5)
    # edit this as needed and paste into ~/.ldaprc

    # server name
    # (for parameterless operation, make sure to include at least this line)
    HOST localhost

    # default search base
    ### multiple namingcontexts found (uncomment one of these lines):
    #BASE dc=lichteblau,dc=com
    #BASE dc=acme,dc=com

    # user to bind as
    #BINDDN <dn>

    # search parameters (uncomment as needed)
    #DEREF never
    #SIZELIMIT 0
    #TIMELIMIT 0

Adjust to taste.

## Interactive usage

After quitting from the editor, one of three things can happen:

- If ldapvi detects that nothing in the file has changed, it will quit.

      No changes.

- Syntax error: Oops. In this case you only have two options:
  You can either re-edit the file and fix the error (type `e`) or give up and quit (type `Q`). As in all menus, type `?` for interactive help.

      Error: Space at beginning of line.
      What now? [eQ?]

- Regular usage: ldapvi has compared your changed file with the original contents and reports on the changes found:

      add: 2, rename: 1, modify: 0, delete: 1
      Action? [yYqQvVebB*rsf+?]

If all is well, you are now in the main loop. Your options are:

Edit - diff - fail. To continue, type `y`.

- `y` -- commit changes

ldapvi will contact the LDAP server apply all changes. First, entries are processed in order of the changed file, then all entries missing from the file are deleted.

- `e` -- open editor again

If any LDAP operation fails while processing the file, ldapvi will stop, report the error, and wait for your choice. To correct such an error, go back to the editor. Your will find all previously processed entries removed from the file, with the failing change at the top. After changing the file, you will be back at the prompt.

    Action? [yYqQvVebB*rsf+?] y
    ldap_modify: Strong(er) authentication required (8)
            additional info: modifications require authentication
    Error at: cn=blub4,dc=lichteblau,dc=com
    add: 0, rename: 0, modify: 1, delete: 0
    Action? [yYqQvVebB*rsf+?]

- `Y` -- commit, ignoring errors

Apply all changes as if invoked with `--continue` ignoring all errors without further warning. (Make sure to save changes to an external file before using this option if they were important. Invalid changes will be lost otherwise.)

Reviewing changes. Before applying the changes, you can inspect them in either LDIF syntax or ldapvi's special changerecord syntax.

- `v` -- view changes as LDIF change records

- `V` -- view changes as ldapvi change records

LDIF syntax is useful if you want to copy and paste changes into a file for later processing with other LDAP tools. ldapvi syntax is usually easier to read (less Base 64) and can also be entered directly into the editor.

- `+` -- rewrite file to include schema comments

This will update the file to include schema comments and re-enter the editor afterwards. This works like the `--may` command line option, but is based on the current contents of the file rather than the original entries, useful when changing the class of an entry.

Note that this command will rewrite the entire file and wipe out all comments present before invoking it.

Logging in. One way to bind to the LDAP server is to specify user name and credentials at startup using command line options or configuration files. The other is to simply start without authentication and login later.

When relying on binding after editing, keep in mind that some information will not be returned by the LDAP server unless user authorization permits it in the first place. For example, a `userPassword` can usually only be displayed and edited if you are bound as that user or root upon startup.

- `b` -- show login dialog and rebind

This option will ask for your user name and password (in the case of simple authentication) or various SASL parameters (as requested by the SASL library).

For simple authentication, the user name can be either an DN or a search filter. In the latter case, it must be written with parentheses around it.

    Action? [yYqQvVebB*rsf+?] b

    --- Login
    Type M-h for help on key bindings.

    Filter or DN: (cn=admin)
        Password: ********
    OK, bound as cn=admin,dc=lichteblau,dc=com.

Same example with SASL:

    Action? [yYqQvVebB*rsf+?] b
    SASL/DIGEST-MD5 authentication started

    --- SASL login
    Type M-h for help on key bindings.

     authorization name:
    authentication name: admin
               password: ********
    SASL username: admin
    SASL SSF: 128
    SASL installing layers
    Bound as authzid=, authcid=admin.

Note that all non-password values are saved into `~/.ldapvi_history`, so after entering them once, "cursor up" or incremental search (`C-r`) will save you from having to type them again.

- `B` -- toggle SASL

Use this key to switch between simple authentication and SASL at run time:

    Action? [yYqQvVebB*rsf+?] B
    SASL authentication enabled.
    SASL mechanism: DIGEST-MD5 (use '*' to change)
    Type 'b' to log in.

- `*` -- set SASL mechanism

When the SASL dialog is shown, it is too late to switch the SASL mechanism. Type `*` to set it at run time.

    Action? [yYqQvVebB*rsf+?] *
    SASL mechanism: DIGEST-MD5
    Type 'b' to log in.

"Oops". You can skip one change to proceed with the rest.

- `s` -- skip one entry

Removes the topmost entry from the data file.

- `f` -- forget all deletions

This is useful if you have deleted entries from the file accidentally (or intentionally) and still want to commit other change records in the same file.

This option does not affect explicit deletion records.

Bailing out. There is a gentle way to quit and a hard one.

- `q` -- save changes as LDIF and quit

- `Q` -- discard changes and quit

`q` will save your changes to a new file, print the file name, and quit only then. This way your changes do not get lost and you can feed them into ldapmodify at a later time.

    Action? [yYqQvVebB*rsf+?] q
    Your changes have been saved to ,ldapvi-xenon-20094.ldif.

## Command line arguments

Normal `ldapvi` invocations look similar to `ldapsearch`(1), taking an optional search filter and zero or more attribute descriptions as unnamed arguments.

    ldapvi [OPTION]... [FILTER] [AD]...

In addition, ldapvi can also simulate behaviour of other LDAP command line tools, e.g. as an interactive substitute for ldapmodify or even for a simple non-interactive usage. See below under command line tool compatibility.

Index of options.

| `-h` |  | `--host` |  | LDAP server to connect to
|  |  | `--bind` |  | Disable or enable SASL
|  |  | `--bind-dialog` |  | Interactive login dialog
| `-D` |  | `--user` |  | User to bind as (simple bind)
| `-w` |  | `--password` |  | User's password
| `-I` |  | `--sasl-interactive` |  | Use SASL Interactive mode
| `-Q` |  | `--sasl-quiet` |  | Use SASL Quiet mode
| `-O` |  | `--sasl-secprops` |  | SASL security properties
| `-R` |  | `--sasl-realm` |  | SASL realm
| `-U` |  | `--sasl-authcid` |  | SASL authentication identity
| `-X` |  | `--sasl-authzid` |  | SASL authorization identity
| `-Y` |  | `--sasl-mech` |  | SASL mechanism
| `-b` |  | `--base` |  | Search base
| `-s` |  | `--scope` |  | Search scope
| `-S` |  | `--sort` |  | Search control
| `-d` |  | `--discover` |  | Auto-detect naming contexts
| `-o` |  | `--class` |  | Class to add
| `-m` |  | `--may` |  | Show schema comments.
|  |  | `--add` |  | Treat attrval records as new entries to add.
| `-A` |  | `--empty` |  | Start with empty file
| `-a` |  | `--deref` |  | Alias dereferencing mode
|  |  | `--config` |  | Print parameters in ldap.conf syntax
| `-c` |  | `--continue` |  | Ignore LDAP errors and continue
|  |  | `--encoding` |  | The encoding to allow
| `-M` |  | `--managedsait` |  | manageDsaIT control
| `-Z` |  | `--starttls` |  | Require startTLS.
|  |  | `--tls` |  | Level of TLS strictness
|  |  | `--read` |  | Read this entry
| `-v` |  | `--verbose` |  | Note every update
| `-H` |  | `--help` |  | Print usage information
|  |  | `--unpaged-help` |  | Disable pager for --help
|  |  | `--deleteoldrdn` |  | Delete the old RDN
|  |  | `--ldif` |  | Use LDIF syntax internally
|  |  | `--ldapvi` |  | Use ldapvi syntax externally
|  |  | `--out` |  | Print entries
|  |  | `--in` |  | Load change records
|  |  | `--delete` |  | Edit a delete record
|  |  | `--rename` |  | Edit a rename record
|  |  | `--ldapsearch` |  | Print entries noninteractively
|  |  | `--ldapmodify` |  | Apply change records noninteractively
|  |  | `--ldapdelete` |  | Delete an entry noninteractively
|  |  | `--ldapmoddn` |  | Rename an entry noninteractively

### Connection parameters

The `host` option specifies the LDAP server to connect to. It is required for startup (unless specified in a configuration file).

`-h` | `--host` hostname | LDAP server to connect to

Three styles of host name are recognized.

- A domain name.
- `ldap://hostname[:port]`
- `ldaps://hostname[:port]`

To specify a port number or tunnelling through SSL, the URL form must be used.

### Authentication

Note that you can also bind to the server interactively after performing the search using the `b` command.

| `--bind` simple|sasl | Disable or enable SASL
| Initially, ldapvi defaults to simple authentication. If any SASL parameter is given, the mode is set to `sasl`. Conversely, `-D` sets it to `simple`. This parameter sets it explicitly.

| `--bind-dialog` never|auto|always | Interactive login dialog

This option controls whether ldapvi will ask for authentication parameters interactively.

The default is `auto`, which means that simple binds will be done interactively if a user name is provided but no password, and similarly for SASL.

`always` forces display of a login dialog even if defaults are known. `never` skips the login dialog completely.

Note that this option has effect only for the initial bind operation. In the main loop, all binds are done interactively.

See also: `-Q` and `-I`.

`-D` | `--user` dn|filter | User to bind as (simple bind)

The user name can be specified as a distinguished name

    uid=foo,ou=bar,dc=acme,dc=com

or as a search filter

    (uid=foo)

Note the use of parenthesis, which can be omitted from search filters usually but are required here. For this searching bind to work, your client library must be configured with appropriate default search parameters.

Exactly one entry must match.

`-w` | `--password` secret | User's password
| Beware that passwords in a simple bind are transmitted in clear text unless the connection is encrypted through SSL. This option is valid for both simple authorization and SASL.

SASL authentication. If any of the SASL arguments is specified, ldapvi uses SASL by default.

If you are unsure which SASL parameters to try for your server, start with `-Y DIGEST-MD5 -U username`.

`-I` | `--sasl-interactive` | Use SASL Interactive mode
| Always ask for SASL parameters interactively. This option is an alias for `--bind-dialog always` and `--bind sasl`.

`-Q` | `--sasl-quiet` | Use SASL Quiet mode
| Never ask for SASL parameters interactively at all. This option is an alias for `--bind-dialog never` and `--bind sasl`.

`-O` | `--sasl-secprops` properties | SASL security properties
| OpenLDAP's slapd.conf(5) manual page has some information on these (search for sasl-secprops).

`-R` | `--sasl-realm` realm | SASL realm
| The SASL realm [Cyrus SASL documentation].

`-U` | `--sasl-authcid` username | SASL authentication identity
| The authentication ID [Cyrus SASL documentation]. (Basically, your username.)

`-X` | `--sasl-authzid` u:username|dn:dn | SASL authorization identity
| The authorization ID [Cyrus SASL documentation]. (See SASL Proxy Authorization [openldap.org] in the OpenLDAP Administrator's Guide.)

`-Y` | `--sasl-mech` mechanism | SASL mechanism
| The SASL mechanism to use. Possible values depend on the server, but `DIGEST-MD5` support is mandated by RFC 2829 for all servers using passwords.

### Search parameters

Where and how to look for entries.

`-b` | `--base` dn | Search base
| The entry at which to start the search. Conflicts with `--discover`.

`-s` | `--scope` base|one|sub | Search scope

- `base`: Retrieve at most the entry at `base`.
- `one`: Search for entries exactly one level below `base`.
- `sub`: Search the entire subtree starting at `base`.

`-S` | `--sort` keys | Search control
| Use the search control to instruct the server to sort results on keys. ldapvi will fail if the server does not support the control. Unfortunately, few servers do.

### Handy parameters

`-d` | `--discover` | Auto-detect naming contexts
| With this option, ldapvi will first read the root DSE, then repeat the search for each naming context found and present the concatenation of all search results. Conflicts with `--base`. With `--config`, show a `BASE` configuration line for each context.

`-o` | `--class` objectClass | Class to add

Read the server's schema for the specified objectclass to find out which attributes an entry of this class must have and can have. Then open the editor with an entry template containing one line for each such attribute the user needs to fill out. Optional attributes are included as comments.

The option can be repeated to include additional, auxiliary classes.

A distinguished name for the entry template can be specified using `--base`.

`-m` | `--may` | Show schema comments.
| Show schema comments for existing entries. Specificially, show optional attributes as comments, if not present.

### Miscellaneous options

| `--add` | Treat attrval records as new entries to add.
| This option applies only to the `--in`/`--ldapmodify` mode: Add new entries (default would be to replace existing attributes).

`-A` | `--empty` | Start with empty file
| Do not search, start with an empty file instead. This is the little brother of the `--class` parameter, which is more interesting for the original use case of this option, adding new entries. However, it is still useful because allows the interactive entry of `delete`, `modify`, or `rename` changerecords.

`-a` | `--deref` never|searching|finding|always | Alias dereferencing mode
| Default is `never`.

| `--config` | Print parameters in ldap.conf syntax
| Print a configuration file in standard `ldap.conf` syntax which reflects current command line arguments. The file is written to standard output. Use this option to create an initial configuration file after experimenting with search parameters. Useful in conjunction with `--discover`.

`-c` | `--continue` | Ignore LDAP errors and continue
| When LDAP errors occur while applying changes, print the messages but continue processing. This mode can also be specified interactively using the 'Y' key.

| `--encoding` ASCII|UTF-8|binary | The encoding to allow

This option controls how ldapvi deals with non-ASCII bytes in attribute values.

- `ASCII`: If an attribute value contains any non-ASCII character, use Base 64 encoding for this value
- `UTF-8`: Use auto-detection. If an attribute value parses as correct UTF-8, pass it through to the file or terminal unchanged. Otherwise, fall back to Base 64. Mark the file's encoding as UTF-8 for Emacs and VIM.
- `binary`: Do not inspect attribute values at all, let arbitrary data through, including zero bytes. Do not use Base 64.

The default is `UTF-8`.

`-M` | `--managedsait` | manageDsaIT control
| Use this option to edit referral entries.

`-Z` | `--starttls` | Require startTLS.
| After opening an unencrypted LDAP connection, use startTLS to enable SSL. (This is an alternative to LDAP connections tunnelled through SSL, specified using `ldaps://` URLs.)

| `--tls` never|allow|try|strict | Level of TLS strictness
| This option controls the level of certificate checking strictness. Default is `try`. fixme: If anyone has a concise description of what these values mean rather than the wishy-washy explanations I can find right now, please tell. Thanks. (Basically, if you want to use SSL but your certificate is self-signed, you might want to experiment with something less than `try`.)

| `--read` DN | Read this entry

A rather trivial option. It means the same as:

    -b DN -s base '(objectclass=*)' + *

`-v` | `--verbose` | Note every update
| Print the distinguished name of every entry as it is being processed.

`-H` | `--help` | Print usage information
| Print help on command line arguments.

| `--unpaged-help` | Disable pager for --help
| Disable the use of a pager for `--help`.

| `--deleteoldrdn` | Delete the old RDN
| When changing the relative distinguished name to a new attribute value, delete the old attribute value instead of keeping it. This option applies only to the `--rename` mode.

| `--ldif` | Use LDIF syntax internally
| Use standard LDIF syntax in the editor. The default is to use ldapvi syntax.

| `--ldapvi` | Use ldapvi syntax externally
| Use ldapvi syntax when reading and writing files. The default is to use LDIF syntax.

### Command line tool compatibility

ldapvi invocation modes inspired by LDAP command line tools:

| `ldapvi --out [OPTION]... [FILTER] [AD]...` |  |  | Print LDIF, similar to ldapsearch
| `ldapvi --in [OPTION]... [FILENAME]` |  |  | Load LDIF change records, similar to ldapmodify
| `ldapvi --delete [OPTION]... DN...` |  |  | Edit delete records, similar to ldapdelete
| `ldapvi --rename [OPTION]... DN1 DN2` |  |  | Edit rename records, similar to ldapmodrdn

All of these are more verbose and interactive than the standard command line tools. There are additional aliases for closer compatibility:

| `--ldapsearch` |  |  | Short for `--quiet --out`
| `--ldapmodify` |  |  | Short for `--noninteractive --in`
| `--ldapdelete` |  |  | Short for `--noninteractive --delete`
| `--ldapmoddn` |  |  | Short for `--noninteractive --rename`

Please keep in mind that all these invocation modes are only meant to imitate LDAP command line tools as far as possible while still working within the design of ldapvi. An option-by-option emulation of the OpenLDAP command line tools is not a goal, and minor differences in look and feel are to be expected.

Format conversion.

| `ldapvi --in | cat` |  |  | Convert LDIF on stdin to ldapvi format on stdout
| `ldapvi --in --ldif --ldapvi | cat` |  |  | Convert ldapvi format on stdin to LDIF on stdout

## Configuration file format

The are two ways to configure ldapvi. It has its own configuration files `~/.ldapvirc` (or `/etc/ldapvi.conf`) which support multiple configuration profiles and all of its custom options. Alternatively, it can fall back to `libldap`'s standard configuration files, `/etc/ldap.conf` and `~/.ldaprc`.

Files and profiles. On startup, ldapvi will first look for `~/.ldapvirc`, or `/etc/ldapvi.conf` if the former does not exist.

- If `--profile` name is specified at the command line, one of these configuration files must exist and it must contain the named profile. Otherwise ldapvi quits with an error.
- If no `--profile` was given, ldapvi looks for a profile called `default`. If no such profile can be found or the files do not exist, ldapvi falls back to `libldap` configuration files.
- By default, if a profile is used, it suppresses loading of the `libldap` configuration files `/etc/ldap.conf` and `~/.ldaprc`. If you want these files to take effect anyway, you can set `ldaprc: yes` in the profile.

Environment variables. The choice of text editor is made using environment variables. `$VISUAL` and `$EDITOR` are looked up in this order. If neither is set, ldapvi falls back to `vi`.

Profile syntax. The configuration file has ldapvi syntax, except with header lines of the form `profile` name.

Every configuration file option corresponds to a command line parameter.

For the named command line parameters, the long name is used in the configuration. For details, refer to the documentation of each parameter: ➙ host ➙ bind ➙ bind-dialog ➙ user ➙ password ➙ sasl-interactive ➙ sasl-quiet ➙ sasl-secprops ➙ sasl-realm ➙ sasl-authcid ➙ sasl-authzid ➙ sasl-mech ➙ base ➙ scope ➙ sort ➙ discover ➙ class ➙ may ➙ add ➙ empty ➙ deref ➙ continue ➙ encoding ➙ managedsait ➙ starttls ➙ tls ➙ read ➙ verbose ➙ unpaged-help ➙ deleteoldrdn ➙ ldif ➙ ldapvi ➙ out ➙ in ➙ delete ➙ rename ➙ ldapsearch ➙ ldapmodify ➙ ldapdelete ➙ ldapmoddn

The two unnamed kinds of command line arguments are the search filter and the list of attribute descriptions to be returned. The former is named ➙ `filter` in the configuration file. The latter are given as ➙ `ad`, one line each.

For boolean flags, given without a value at the command line, use `yes` as their value in the configuration file. (`no` is also allowed and has no effect.)

Example. With the following configuration file, ldapvi ignores `libldap` configuration, because a `default` profile is given.

- The default profile falls back to `libldap` configuration, but enables schema comments.
- `ldapvi -p otherhost` profile reads all entries from `otherhost`.
- `ldapvi -p adduser` will open a template for a new user entry to be filled in.
- `ldapvi -p root` will retrieve the root DSE with its operational attributes.

    profile default
    ldaprc: yes
    may: yes

    profile otherhost
    host: ldap://otherhost
    discover: yes

    profile adduser
    host: ldap://localhost
    base: uid=FILLMEIN,ou=passwd,dc=lichteblau,dc=com
    class: person
    class: posixAccount

    profile root
    host: ldap://localhost
    base:
    scope: base
    filter: (objectclass=*)
    ad: +

## ldapvi syntax explained

ldapvi syntax is somewhat LDIF-like, but not the same as standard LDIF. This chapter includes examples explaining the difference between the two formats. (A precise, technical definition of ldapvi syntax is given in the next section.)

➙ In interactive editing, the default is ldapvi syntax, explained below, unless overridden using `--ldif`.

➙ For external input and output (see `--in`, `--out`), the default is standard LDIF syntax, unless overridden using `--ldapvi`.

### Basics

Comments are lines starting with a sharpsign:

    # like this

They are allowed both between entries and in the middle of entries (but not in the middle of an attribute value).

Empty lines are used to separate entries:

    0 dc=lichteblau,dc=com
    # don't touch the number!
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    o: lichteblau
    dc: lichteblau

    # more than one empty line would be OK here, too
    add cn=admin,dc=lichteblau,dc=com
    objectClass: simpleSecurityObject
    objectClass: organizationalRole
    cn: admin
    description: LDAP administrator

So far this looks pretty much like LDIF, except for the record key that appears instead of the `dn:` LDIF uses to start an entry.

- Numbers as keys reference entries read from the server. ldapvi uses them to find the original entry when comparing your changed file with its unchanged copy.
- Special key `add` is used for new entries that are to be added to the tree. Except for not having a number key, its syntax is the same as for normal entries.
- In addition, there are special keys for change records: `rename`, `delete`, and `modify`. Change records have special syntax and are explained below in detail.

First though, more on low-level syntax issues.

### Value encodings

All non-empty, non-comment "lines" have the same base syntax: A left-hand and a right-hand side, separated by an optional encoding marker and a space. (Depending on the encoding used, a such a logical line may include newline characters.)

The syntax of the right-hand side depends on the encoding marker. For simple ASCII text without special characters, there are multiple valid encodings that can be used.

This includes the first line of a record with the distinguished name, its base syntax is the same as for the attribute lines. For traditional reasons, ldapvi prints this line without a colon, but that is not a requirement:

    # rather than writing this:
    add cn=admin,dc=lichteblau,dc=com

    # we could have included a colon:
    add: cn=admin,dc=lichteblau,dc=com

    # or actually, this:
    add:; cn=admin,dc=lichteblau,dc=com

Or, vice versa, we could have omitted the colon from those attribute values. ldapvi prints it only so that entries looks for familiar to eyes used to LDIF.

Value encodings are:

- `ad: value`: LDIF-compatible
- `ad:: value`: Base 64
- `ad value`: backslashed (short form)
- `ad:; value`: backslashed (long form)
- `ad:n value`: binary
- `ad:< value`: input from file
- `ad:crypt value`: userPassword {CRYPT}-hash
- `ad:cryptmd5 value`: userPassword {CRYPT}-hash
- `ad:md5 value`: userPassword {MD5}-hash
- `ad:smd5 value`: userPassword {SMD5}-hash
- `ad:sha value`: userPassword {SHA}-hash
- `ad:ssha value`: userPassword {SSHA}-hash

LDIF-compatible encoding. Lines with just a colon and a space are parsed according to normal LDIF rules. Values must be ASCII-only and must not contain zero bytes, LF, or CR. In addition, they must not start with a space, a colon, or a less-than sign.

This encoding allows LDIF-style line folding: If a line ends, and the first character of the next line, if any, is a space, the nextline and the space are elided and parsing continues on the next line.

Note: The language for attribute value lines described above is the common subset of LDIF's attrval-spec and the actual syntax accepted by ldapvi for this value encoding. ldapvi will only ever print out values in this encoding if they follow these rules and hence are valid LDIF, but is more liberal in parsing them. It accepts space, colon, or less-than sign as the first character. It can allow them without ambiguity because, in contrast to LDIF, it does not ignore an arbitrary number of space characters after the colon.

Examples of valid LDIF values:

    # normally, one would write:
    objectClass: posixAccount

    # but with line folding, the same value can be broken into two lines:
    objectClass: posix
     Account

    # in contrast to ldapvi backslashed format, this encoding does not
    # interpret backslashes specially at all:
    cn: this is a value containing a backslash\
     but no newline

Examples of attribute values not representable in LDIF-compatible encoding:

    # must not start with '<':
    # [accepted by ldapvi but not LDIF]
    document: <?xml version="1.0" encoding="UTF-8"><foo/>

    # cannot start with a space:
    # [LDIF would ignore the space]
    foo:  ...

    # must not start with a colon:
    # [accepted by ldapvi but not LDIF]
    foo: ::::::::

    # must not contain non-ASCII bytes:
    surname: Müller

    # cannot represent newlines:
    mail: From david@lichteblau.com Mon Apr  1 00:11:22 2006
    Return-path: <david@lichteblau.com>
    Envelope-to: lists@mail.askja.de

Base 64 encoding. Base 64 can be used just as in LDIF.

    # This is what the XML from above looks like in strict LDIF:
    document:: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPjxmb28vPg==

ldapvi backslashed encoding. This is the format ldapvi falls back to if a value cannot be represented in LDIF-compatible encoding without resorting to Base 64. Any character is valid in this encoding, except for two characters with a special meaning:

- newline terminates the value unless escaped with a backslash
- backslash escapes the following character

Examples of valid backslashed attributes:

    # any funny characters are OK
    document:; <?xml version="1.0" encoding="UTF-8">
    foo:;  ...
    foo:; ::::::::
    surname:; Müller

    # backslash escapes newline
    mail:; From david@lichteblau.com Mon Apr  1 00:11:22 2006\
    Return-path: <david@lichteblau.com>\
    Envelope-to: lists@mail.askja.de

    # No LDIF line folding, however, so this value contains a space:
    cn:; David\
     Lichteblau

Counter-example:

    # Parsable first line, followed by garbage on the second:
    cn:; David
     Lichteblau

Finally, in most situations `:;` can be omitted, leaving just a space between the left-hand and right-hand side of the line. The only exception is a line with an empty left-hand side, which occurs in modify records.

    foo:; bar
    # is the same as:
    foo bar

Binary encoding. Decimal numbers are valid as encoding markers and specify the number of bytes to read after the separating space. No special characters exist in this syntax. This encoding is not meant for interactive use, but for the sake of completeness, here is an example:

    # This entry has two attributes, givenname and cn:
    add cn=admin,ou=passwd,dc=blubba
    givenname:5 Davidcn: admin

File inclusion. The less-than sign tells LDAP to read the contents of another file and use them as the attribute value. This is another LDIF compatibility feature.

    # oops
    add cn=haxor,ou=passwd,dc=blubba
    comment:< file:///etc/passwd

userPassword encodings. The encodings crypt, cryptmd5, md5, smd5, sha, and ssha are designed for the `userPassword` attribute. ldapvi takes the right-hand side as the password, hashes it, and prepends `{ENCODING}` as appropriate.

    userPassword:crypt foo
    # ...could result in:
    userPassword: {CRYPT}KgJTUDs14z57Q
    # ...or:
    userPassword: {CRYPT}UoILyHlM1bPYU

    # cryptmd5 gives $1$ notation
    userPassword:cryptmd5 foo
    userPassword: {CRYPT}$1$iKeAY9mQ$vmo/m/6EoDqRuVtjSqLPr0

    # while the other encodings prepend the marker of the same name:
    userPassword:md5 foo
    userPassword: {MD5}AQ3GmMeoPXozyRU4wsEDXA==

### Change records

Aside from the obvious `add` records mentioned above, there are three change record formats: `rename`, `modify`, and `delete`.

rename records. Like every other record, `rename` states the (original) distinguished name of the name in the header line. Next is one additional line stating the new distinguished name.

The complication is that LDAP's moddn operation needs to be told what to do with the old relative DN if there is a new one. The old one can be kept as an additional attribute value or deleted in favour of the new one.

In ldapvi, this choice is made using the left-hand side of the second line. It is either "add" or "replace".

    # example for "deleteoldrdn" behaviour:
    rename: cn=blub4,dc=lichteblau,dc=com
    replace: cn=blub3,dc=neu,dc=com

    # alternatively, the old RDN can be kept:
    rename: cn=blub3,dc=lichteblau,dc=com
    add: cn=blub4,dc=neu,dc=com

In LDIF, the same changes would be written as:

    # this is LDIF, not ldapvi syntax!
    dn: cn=blub4,dc=lichteblau,dc=com
    changetype: modrdn
    newrdn: cn=blub3
    deleteoldrdn: 1
    newsuperior: dc=neu,dc=com

    dn: cn=blub3,dc=lichteblau,dc=com
    changetype: modrdn
    newrdn: cn=blub4
    deleteoldrdn: 0
    newsuperior: dc=neu,dc=com

modify records. These records state changes to attributes, processed in order. Attributes can be changed by replacing their existing values with a new set of values, adding those values to the existing ones, or removing only selected old values.

Example:

    # Let's add classes, delete a comment and remove cn entirely:
    modify: dc=bar,dc=lichteblau,dc=com
    add: objectClass
    : posixAccount
    : specialAccount
    delete: comment
    : dummy user
    replace: cn

In LDIF, this would be:

    # this is LDIF, not ldapvi syntax!
    dn: dc=bar,dc=lichteblau,dc=com
    changetype: modify
    add: objectClass
    objectClass: posixAccount
    objectClass: specialAccount
    -
    delete: comment
    comment: dummy user
    -
    replace: cn
    -

delete records. These are rather simple, they consist just of the header line.

    delete: dc=acme,dc=com

In LDIF, this would be:

    # this is LDIF, not ldapvi syntax!
    dn: dc=acme,dc=com
    changetype: delete

### Using LDIF syntax

When invoked using `--ldif`, ldapvi will use RFC 2849 syntax instead of its own special syntax.

However, there is a special marker in each entry:

Existing entries are presented for editing with an additional marker on the second line, just after the `dn:` line. This line states the (numeric) key that would appear in the header line if the entry had been printed in ldapvi syntax.

(New entries can have the same line, with `add` as their key, but records with neither `ldapvi-key` nor `changetype` are automatically assumed to be new entries to add.)

Also, `control:` is not supported.

Examples:

    # Existing entry
    dn: dc=lichteblau,dc=com
    ldapvi-key: 0
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    o: lichteblau
    dc: lichteblau

    # New entry
    dn: cn=admin,dc=lichteblau,dc=com
    # next line is optional
    ldapvi-key: add
    objectClass: simpleSecurityObject
    objectClass: organizationalRole
    cn: admin
    description: LDAP administrator

    # Changerecord (standard LDIF syntax)
    dn: cn=blub4,dc=lichteblau,dc=com
    changetype: modify
    delete: seeAlso
    seeAlso: cn=dort
    -

## ldapvi syntax (BNF)

As explained above, ldapvi has its own, vaguely LDIF-inspired syntax. For a precise definition, see `ldapvi-file` below.

LDIF is defined in RFC 2849.

RFC 2849 modifies the language generated by its BNF grammar with the "Notes on LDIF Syntax" (2) and (3) presented there. These notes do not apply to ldapvi syntax as stated: Line folding and comments can be used, but are allowed specificially by our modified BNF, not hacked into the rules later. Line folding is allowed only in LDIF-compatible attribute values and comments. ldapvi files cannot be read line-by-line without knowing the encoding of those lines, since newlines are permitted in certain attribute value representations.

The following rules are used as defined in RFC 2849 and not reproduced here: AttributeDescription, SAFE-STRING, BASE64-STRING, SPACE.

Changes to the RFC 2849 BNF grammar.

    ldapvi-file = vspace ldapvi-record
                  *(vspace SEP vspace ldapvi-record) vspace
    ldapvi-record = ldapvi-attrval-record
    	        / ldapvi-rename-record
    		/ ldapvi-modify-record
    ldapvi-attrval-record = *(comment) dn-spec 1*(*(comment) attrval-spec)
    ldapvi-rename-record = *(comment)
                           "rename" distinguishedName SEP
    		       ("add" / "replace") value-spec SEP
    ldapvi-modify-record = *(comment)
                           "modify" distinguishedName SEP
    		       1*(*(comment) mod-spec)
    mod-spec = ("delete" / "add" / "replace") ad-value SEP
    	   *(value-enc SEP)

    vspace = *(comment / sep)
    comment = "#" *(ldif-char) *(SEP " " *(ldif-char)) SEP

    dn-spec = key distinguishedName SEP
    key = number / "add"
    distinguishedName = value-spec                  ;encoding an RFC 2253 DN

    attrval-spec = AttributeDescription value-spec SEP
    value-spec = value-simple / value-enc
    value-simple = SPACE escaped-string             ;escape CR/LF with '\\\\'
    value-enc = (":;" SPACE escaped-string /        ;ditto
                 ":" SPACE 0*1(SAFE-STRING          ;LDIF compatible
                               *(SEP " " *SAFE-CHAR)) /
                 "::" SPACE BASE64-STRING /         ;ditto
                 ":" number SPACE 0*1(octet) /      ;exactly `number' octets
                 ":<" SPACE url /                   ;only file:// supported
                 ":crypt" SPACE password /          ;for userPassword
                 ":cryptmd5" SPACE password /
                 ":md5" SPACE password /
                 ":smd5" SPACE password /
                 ":sha" SPACE password /
                 ":ssha" SPACE password /
                 ;; other encoding markers reserved
                )

    octet = %x00-ff
    number = 1*(%x30-39)                            ;any decimal number
    ldif-char = unescaped-char / %5c                ;any except for CR/LF.
    unescaped-char = %x00-%x09 / %x0a-%x0c / %x0d-%5b / %x5d-%ff
    escaped-string = *(unescaped-char / %x5c %x0a / %x5c %x0d / %x5c %x5c)
    password = 0*ldif-char
    SEP = LF                                        ;this is a unix program!

    ad-value = value-spec  ;parsing as an AttributeDescription

## Reporting bugs

Please report bugs to the ldapvi mailing list for archival purposes.

Address: ldapvi@lists.askja.de