In this article and the next one we will explore the printer list PHP code to demonstrate how easy it can be to extend OES for your own nefarious purposes. After that we will wrap up by demonstrating a couple of other utilities made possible by our project.
All the code that we are going to explain is kept in the iprint_lists.php file contained in the ttp_printer_lists_core_1_2.zip download that was attached to Part VI above. To follow along you will need to open the php file in a text editor that supports displaying line numbers and, ideally, syntax highlighting and different types of line endings. We will navigate the code by referring to a specific line number, so that is key. The syntax highlighting will help by coloring different parts of the code differently depending upon their function. Being able to read different types of line endings will stop everything from running together when the file is read on different platforms.
For Windows, I recommend Notepad and on Mac OS X BBEdit (which has a free trial mode) is the gold standard. If you are on Linux ,you likely already have strong opinions about text editors or will have very soon.
I picked PHP to write the web portion of the printer lists for several reasons:
The code is not complicated. We can think of it in three stages:
Every item in the printer row is also included as part of the regular iPrint web page. Instructions on creating static pages with the same information have been part of the iPrint documentation since iPrint was released two decades ago. We are not building something completely new, we are adding on to value that already exists.
If there is one message I would like to send out with this series to the OES team and my colleagues who use OES, it is that giving administrators and programmers more places where we can extend existing functionality ourselves would be greatly appreciated (Commercial Over).
The first third of our PHP file is just getting the environment ready for the real work. At the very top (lines 1–11) is the start of the HTML which contains no PHP code. Below that is the start of the PHP section with some basic documentation on the lists (a shorter version of this series). This documentation is in the form of code comment lines, and it should be colored differently from other code if your text editor supports syntax highlighting.
The first real PHP code starts on line 51, which is where a bunch of variables get set. In PHP all variables begin with a dollar sign (i.e. “$variablename”). The variables that need to be changed / configured for individual sites and printer list installations come first, from lines 51–92. This section is longer than its actual function because there is a lot of commentary on the code. We covered these variables in Part VI so there is no need to do so here.
Below these variables are another set that should not be modified. These are set the same way for every printer list configuration.
The most significant portion of this code where we set up the LDAP connection. There are some lessons we can learn from it that extend far beyond this project.
With a few notable exceptions such as the libraries for Java and .NET/C#, virtually every programming language’s library for dealing with LDAP is just a thin shim to the C LDAP libraries, based on libldap from the OpenLDAP project. Therefore, if you know how the C LDAP libraries work, figuring out how to use LDAP with a new programming language is generally just an exercise in figuring out the language syntax.
As a side-note, there used to be a separate eDirectory LDAP C library but as of 2016 eDirectory developers are advised to use the OpenLDAP libraries with a separate extension added separately that provides the eDirectory-added functionality. For more on that, see here.
Despite LDAP being an open standard, the three major implementations of it (eDirectory, Active Directory, OpenLDAP) manage to assume entirely different layouts for a standard organization. Most of the examples you will see on the Internet for using LDAP libraries in programs are not oriented towards eDirectory. That is okay, just remember the basics of LDAP notation and eDirectory and you should be fine.
Here is the basic LDAP connection code, starting at line 141:
$edirConn = ldap_connect($eDirectoryLdapPrefix . $eDirectoryServer, $eDirectoryLdapPort);
ldap_set_option($edirConn, LDAP_OPT_NETWORK_TIMEOUT , $eDirectoryTimeout);
$edirBind = ldap_bind($edirConn, $eDirectoryProxyUser, $eDirectoryProxyPassword);
debugError($debugLevel, true , "Sorry, we are unable to retrieve your printer information at this time.", "Unable to connect to LDAP Server at $eDirectoryServer on port $eDirectoryLdapPort", "Error is: " . ldap_error($edirConn));
Counting the three lines of the “if” statement as one, we have four lines of code that you will use over and over in any program you write that uses LDAP.
In the first line we establish an LDAP connection object. The LDAP connection object is the basic object we will use to communicate the server. You will find it implemented in most LDAP programming regardless of the programming language. It is important to realize that, when we create this object, we have not actually done anything except fill in the information we will use when we eventually do reach out to the server. At this stage we have not verified any information or tried to use the LDAP protocol on the network. We have to work with it a little more.
The ldap_connect function which creates the connection takes two arguments–a LDAP URL and a port. Notice that the port is not usually part of the URL for this function (i.e. no colon at the end of the URL). Traditionally LDAP listens on port 389 for clear connections and 636 for SSL connections, similar to how web servers listen on port 80 and 443 for the same purposes.
However, there is a notable exception that is not directly addressed in our code here–TLS. With TLS (the evolved form of SSL) we can have an encrypted connection on the same port as the clear connection. That is, if were using TLS we would have powerful encryption but we could still use port 389. In this project our code does not support TLS but we could probably extend it to do so easily (Note To Self: Subject For A Later Cool Solution).
The ldap URL is similar to that for the HTTP web protocol in that we start with the protocol name (ldap or ldaps for clear and encrypted respectively), then “://” followed by the server name or IP. In short, it is a standard URL such as “ldaps://server.company.com”. Notice that on lines 110–113 we set the LDAP prefix value based on whether SSL was configured and then we add the prefix to the server name when we create the connection (in PHP, variables separated by a period are combined into one string).
The second line sets an option on the object. In this case we are just setting a timeout value on the LDAP connection so that we can tolerate slowness on the network. There are a lot of LDAP-specific options we can set for our server connection, and they are all constant values represented by strings in the form of LDAP_OPT_SOMETHING_OR_OTHER . The option strings get set to binary constants when PHP sets them and the server receives them, but we can paper over that detail. In your own program or environment you may want to set different options.
A list of options supported by PHP can be found here.
The third line is the LDAP bind. This is where we actually try to connect to and log into the LDAP server, and it is the first real part of the code where something might go wrong. If we succeed, we can use the LDAP connection to grab all the information we need from eDirectory. If we fail, we are stuck. The function that creates the bind takes as arguments the LDAP connection, a user DN, and a password.
The bind call does not know which connection to use for the bind, so we have to pass that as an argument, which I have always found a little odd – I would generally expect the bind function to be a method of the connection object, but the given is how the LDAP C library works and so we go with it (the reason probably boils down to how C pointers work). The proxy user we use to connect is in LDAP DN format because multiple LDAP users could have the same short name attribute (and the ways in which other programs implement a login with a short name is worth an article by itself). The password is, of course, the proxy user’s password.
So what happens if the bind fails? Well, that is where the last line (actually three lines) comes in. If the bind failed, then the bind call will return “false”, which is not tremendously helpful. Fortunately, the LDAP server remembers its communication with your specific connection. We can ask it about the last error that it saw, which would be the bind error. The LDAP error is a complex object with several representations including a numeric code. All we care about is the text of the error, so we use the ldap_error function to get that and combine it with some other diagnostic information in our own combined error message (We will cover our approach to error messages later in this series).
Assuming that the bind actually succeeded, we move on into the rest of the code. In the next part of this series we will skip forward to the third portion of the php file to show how we display our results. Then we will wrap up our discussion of the code by studying the mode code in the middle and perhaps looking at some tidbits swirling around the edges.