Archive for the ‘Computers & Internet’ category

.NET installers

May 26th, 2009

Setup ScreenIt’s always been one of the most important, yet often overlooked part of a Windows application, the installation process.

I started off in Visual Studio using the default setup process project and for the most part it worked absolutely fine. In VS2005 I’d build my project, I’d create a setup.exe wrapper around my MSI, then bundle it in a self extracting zip file to launch setup.exe. That worked for me and I liked the setup.

Then came Visual Studio 2008 and that all changed. Microsoft didn’t seem to change too much in the setup process yet I was finding my setup projects were crashing constantly and consistently. I discovered that with the 2008 setup system the .exe wrapper was calling the msi and immediately exiting allowing the .msi to carry on. Yet the WinZip self extracting .exe tool was waiting for the .exe to finish before cleaning up the files. Effectively as soon as setup.exe finished, WinZip deleted the .msi even if it was still being used.

After hours of research I couldn’t find a way to change this so I decided to look for decent applications out there that not only would replicate what the Visual Studio setup project would do, but actually enhance it.

My requirements were pretty straight forward.

  1. It must detect pre-requisites such as .NET Framework, screen resolutions, etc
  2. Allow me to put custom settings in the registry or shortcut folders
  3. Allow me to create custom components and features that the user can toggle those features on or off
  4. I wanted to be able to associate file extensions with my applications
  5. I wanted to be able to perform actions after an installation or in uninstall (such as add a component to the Visual Studio toolbox)

Nice to haves would be to allow me to have licensing integrated at the installer level rather than the application level, customising the interface, etc

My research led me to a number of applications, but the only one that grabbed me was an application called Advanced Installer by Caphyon.

Advanced Installer

This software comes in a number of different versions, most notably a free version too.  I elected for the professional version though but the comparisons are available from their website.

What amazed me about this software was it was so simple to use and build my installer yet the interface to do so was very straight forward, including a number of built in wizards for creating shortcuts, file extension associations, etc.

So much so that I migrated all of my Windows apps to Advanced Installer as soon as I could.

The current version, just released, is version 7.0, which includes an installer for the Windows Mobile Platform.  For me this release is going to solve a problem I hadn’t even researched.  Basically I am currently developing a lightweight app for Windows that I have thought in passing would be perfect for Windows Mobile too, yet I hadn’t actually thought how to deploy for Windows Mobile as I’ve never written a Windows Mobile app before.

So into my Inbox this morning, pops an email from Caphyon telling me about version 7.0 and it’s new features.  I haven’t tested this feature yet, but if it works as well as the rest of the application does, this is going to solve hours or even days in working out how to deploy my applications on the Windows Mobile Platform.

I’m looking forward to their new GUI interface enhancements to the end-user installation screens as well as the MSIE 8 detection. The one other feature that I’m looking forward to is, in version 7.0 they have added a feature to allow detection of free ports on the PC on installation.  This will solve many issues with connectivity that user’s face in their own apps all the time!

Basically guys, if you want a decent installer for Windows applications, and you don’t want to spend a fortune, there is only one way to go… Advanced Installer.

How to truely make a PictureBox background transparent

May 26th, 2009

Recently I wanted to overlay a PictureBox over the top of a WebBrowser control in C# for Windows Forms. I added a transparent PNG to a PictureBox and set the Background to transparent.

However the problem with this approach is the Background of the PictureBox isn’t truely transparent, it simply inherits the background colour or background image of the control in the layer beneath it.

So in Visual Studio I have the following (note the scroll bar at the bottom right hand corner which can’t be seen or accessed due to the image overlaying it)

untitled-1

However I found a small function that will take the colour of the PictureBox at pixel position x=0, y=0 (which in my image happens to be transparent and apply that to all other transparent elements.

public static System.Drawing.Drawing2D.GraphicsPath Transparent(Image im)
        {
            int x;
            int y;
            Bitmap bmp = new Bitmap(im);
            System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
            Color mask = bmp.GetPixel(0, 0);

            for (x = 0; x <= bmp.Width - 1; x++)
            {
                for (y = 0; y <= bmp.Height - 1; y++)
                {
                    if (!bmp.GetPixel(x, y).Equals(mask))
                    {
                        gp.AddRectangle(new Rectangle(x, y, 1, 1));
                    }
                }
            }
            bmp.Dispose();
            return gp;

        }

Then in your form load event handler

System.Drawing.Drawing2D.GraphicsPath gp = Resources.Images.Transparent(pictureBox1.Image);
pictureBox1.Region = new System.Drawing.Region(gp);

And voila, when you compile your application you'll get

untitled-2

Installing a SSL certificate on your Domain Controller

May 12th, 2009

We’ve recently had the need to install an SSL certificate on our Domain Controllers.  Without SSL you cannot change Active Directory passwords or create user accounts (with a password) over LDAP.

There are two Microsoft Documents that help this process along

  1. A KB article entitled “How to enable LDAP over SSL with a third party certification authority
  2. A TechNet article “Advanced Certificate Enrollment and Management

Certificate Authority

In the first instance, we are installing a standalone CA (certificate authority) and a seperate Windows Server 2008 system.

Generate the Request

On the Domain controller we create a file called request.inf

;----------------- request.inf -----------------
[Version]
Signature="$Windows NT$
[NewRequest]
Subject = "CN=<DC fqdn>" ; replace with the FQDN of the DC
KeySpec = 1
KeyLength = 1024
; Can be 1024, 2048, 4096, 8192, or 16384.
; Larger key sizes are more secure, but have
; a greater impact on performance.
Exportable = TRUE
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
[EnhancedKeyUsageExtension]
OID=1.3.6.1.5.5.7.3.1 ; this is for Server Authentication
;-----------------------------------------------

From the command line we then run

certreq -new request.inf request.req

A file called request.req is created

Change the Request Settings

We then copy the request.req file over to the CA.  This is where things get more complicated.

Again from the command line we need to run

certreq -attrib "CertificateTemplate:DomainController" request.req

This will give us a request ID, this is important, make a note of it.

The next stage mentions we need to copy the .asn file from the domain controller to the CA.  Hang on a moment, what .asn file and where is it?

Microsoft gloss over this and it took us around 2 hours to find the answer to this, it’s mentioned in Appendix 2, basically create and run this vbs script and it will create your .inf and your .asn files for you.

Set oArgs = WScript.Arguments
Set oShell = WScript.CreateObject("WScript.Shell")
'
' Parse command line
'
if oArgs.Count < 1 then
    sTemplateName = "DomainController"
    sType = "E"
else
    if ((oArgs(0) = "-?") or (oARgs.Count < 2)) then
        Wscript.Echo "Usage: reqdccert.vbs [Templatename] [Type]"
        Wscript.Echo "[Templatename] is the name of a V2 template"
        Wscript.Echo "[Type]         can be E for Email and A for Authentication certificate"
        Wscript.Echo "If no option is specified, the DomainController certificate template is used."
        Wscript.Quit 1
    else
        sTemplateName = oArgs(0)
                sType = oArgs(1)
    end if
end if
Set oFilesystem = CreateObject("Scripting.FileSystemObject")
Set objSysInfo = CreateObject("ADSystemInfo")
Set objDC = GetObject("LDAP://" & objSysInfo.ComputerName)
sGUID = objDC.GUID
sDNShostname = objDC.DNShostname
sHostname = objDC.cn
'##############################################################################
'
' Create the ASN.1 file
'
'##############################################################################
Dim aASNsubstring(2, 5)
Const HEX_DATA_LENGTH = 1
Const ASCIIDATA = 2
Const HEXDATA = 3
Const HEX_BLOB_LENGTH = 4
Const HEX_TYPE = 5
aASNsubstring(0, ASCIIDATA) = sDNShostname
aASNsubstring(0, HEX_TYPE) = "82"
'
' Convert DNS name into Hexadecimal
'
For i = 1 to Len(aASNsubstring(0, ASCIIDATA))
    aASNsubstring(0, HEXDATA) = aASNsubstring(0, HEXDATA) & _
                                    Hex(Asc(Mid(aASNsubstring(0, ASCIIDATA), i, 1)))
Next
aASNsubstring(0, HEX_DATA_LENGTH) = ComputeASN1 (Len(aASNsubstring(0, HEXDATA)) / 2)
'
' Build the ASN.1 blob for DNS name
'
sASN = aASNsubstring(0, HEX_TYPE) & _
       aASNsubstring(0, HEX_DATA_LENGTH) & _
       aASNsubstring(0, HEXDATA)
'
' Append the GUID as other name
'
if (sType = "E") then
    aASNsubstring(1, HEXDATA) = sGUID
    aASNsubstring(1, HEX_TYPE) = "A0"
    aASNsubstring(1, HEX_DATA_LENGTH) = ComputeASN1 (Len(aASNsubstring(1, HEXDATA)) / 2)
    sASN = sASN & _
           "A01F06092B0601040182371901" & _
           aASNsubstring(1, HEX_TYPE) & _
           "120410" & _
           aASNsubstring(1, HEXDATA)
end if
'
' Write the ASN.1 blob into a file
'
Set oFile = oFilesystem.CreateTextFile(sHostname & ".asn")
'
' Put sequence, total length and ASN1 blob into the file
'
oFile.WriteLine "30" & ComputeASN1 (Len(sASN) / 2) & sASN
oFile.Close
'
' Use certutil to convert the hexadecimal string into bin
'
oShell.Run "certutil -f -decodehex " & sHostname & ".asn " & _
                                       sHostname & ".bin", 0, True
'
' Use certutil to convert the bin into base64
'
oShell.Run "certutil -f -encode " & sHostname & ".bin " & _
                                    sHostname & ".b64", 0, True
'##############################################################################
'
' Create the INF file
'
'##############################################################################
Set iFile = oFilesystem.OpenTextFile(sHostname & ".b64")
Set oFile = oFilesystem.CreateTextFile(sHostname & ".inf")
oFile.WriteLine "[Version]"
oFile.WriteLine "Signature= " & Chr(34) & "$Windows NT$" & Chr(34)
oFile.WriteLine ""
oFile.WriteLine "[NewRequest]"
oFile.WriteLine "KeySpec = 1"
oFile.WriteLine "KeyLength = 1024"
oFile.WriteLine "Exportable = TRUE"
oFile.WriteLine "MachineKeySet = TRUE"
oFile.WriteLine "SMIME = FALSE"
oFile.WriteLine "PrivateKeyArchive = FALSE"
oFile.WriteLine "UserProtected = FALSE"
oFile.WriteLine "UseExistingKeySet = FALSE"
oFile.WriteLine "ProviderName = " & Chr(34) & _
                "Microsoft RSA SChannel Cryptographic Provider" & Chr(34)
oFile.WriteLine "ProviderType = 12"
oFile.WriteLine "RequestType = PKCS10"
oFile.WriteLine "KeyUsage = 0xa0"
oFile.WriteLine ""
oFile.WriteLine "[EnhancedKeyUsageExtension]"
oFile.WriteLine "OID=1.3.6.1.5.5.7.3.1"
oFile.WriteLine "OID=1.3.6.1.5.5.7.3.2"
oFile.WriteLine ";"
oFile.WriteLine "; The subject alternative name (SAN) can be included in the INF-file"
oFile.WriteLine "; for a Windows 2003 CA."
oFile.WriteLine "; You don't have to specify the SAN when submitting the request."
oFile.WriteLine ";"
oFile.WriteLine "[Extensions]"
iLine = 0
Do While iFile.AtEndOfStream <> True
    sLine = iFile.Readline
    If sLine = "-----END CERTIFICATE-----" then
        Exit Do
    end if
    if sLine <> "-----BEGIN CERTIFICATE-----" then
        if iLine = 0 then
            oFile.WriteLine "2.5.29.17=" & sLine
        else
            oFile.WriteLine "_continue_=" & sLine
        end if
        iLine = iLine + 1
    end if
Loop
oFile.WriteLine "Critical=2.5.29.17"
oFile.WriteLine ";"
oFile.WriteLine "; The template name can be included in the INF-file for any CA."
oFile.WriteLine "; You don't have to specify the template when submitting the request."
oFile.WriteLine ";"
oFile.WriteLine ";[RequestAttributes]"
oFile.WriteLine ";CertificateTemplate=" & sTemplateName
oFile.Close
iFile.Close
'##############################################################################
'
' Create the certreq.exe command-line to submit the certificate request
'
'##############################################################################
Set oFile = oFilesystem.CreateTextFile(sHostname & "-req.bat")
oFile.WriteLine "CERTREQ -attrib " _
                 & Chr(34) & "CertificateTemplate:" & sTemplateName _
                 & Chr(34) & " " & sHostname & ".req"
'
' The GUID structure needs to be reconstructed. The GUID is read
' as a string like f4aaa8576e6828418712b6ca89fbf5bc however the
' format that is required for the certreq command looks like
' 57a8aaf4-686e-4128-8712-b6ca89fbf5bc. The bytes are reordered
' in the following way:
'
'                            11111111112222222222333
'             Position 12345678901234567890123456789012
'                      |------|--|--|--|--------------|
' Original GUID:       f4aaa8576e6828418712b6ca89fbf5bc
'
'                            11 1 1111 1112 222222222333
'             Position 78563412 1290 5634 7890 123456789012
'                      |------- |--- |--- |--- |----------|
' Reformatted GUID:    57a8aaf4-686e-4128-8712-b6ca89fbf5bc
'
oFile.WriteLine "REM "
oFile.WriteLine "REM !!! Only valid for Windows 2003 or later versions !!!"
oFile.WriteLine "REM If you do not specify certificate extensions in the *.INF file"
oFile.WriteLine "REM they can be specified here like the following example"
oFile.WriteLine "REM "
oFile.WriteLine "REM CERTREQ -submit -attrib " _
                 & Chr(34) & "CertificateTemplate:" & sTemplateName _
                 & "\n" _
                 & "SAN:guid=" _
                 & Mid(sGUID, 7, 2) _
                 & Mid(sGUID, 5, 2) _
                 & Mid(sGUID, 3, 2) _
                 & Mid(sGUID, 1, 2) & "-" _
                 & Mid(sGUID, 11, 2) _
                 & Mid(sGUID, 9, 2) & "-" _
                 & Mid(sGUID, 15, 2) _
                 & Mid(sGUID, 13, 2) & "-" _
                 & Mid(sGUID, 17, 4) & "-" _
                 & Mid(sGUID, 21, 12) _
                 & "&DNS=" & sDNShostname & Chr(34) & " " & sHostname & ".req"
oFile.Close
'##############################################################################
'
' Create the certificate verification script
'
'##############################################################################
Set oFile = oFilesystem.CreateTextFile(sHostname & "-vfy.bat")
oFile.WriteLine "certutil -viewstore " & Chr(34) & objDC.distinguishedname & _
                "?usercertificate" & chr(34)
oFile.Close
'##############################################################################
'
' Compute the ASN1 string
'
'##############################################################################
Function ComputeASN1 (iStrLen)
    If Len(Hex(iStrLen)) Mod 2 = 0 then
        sLength = Hex(iStrLen)
    else
        sLength = "0" & Hex(iStrLen)
    end if
    if iStrLen > 127 then
        ComputeASN1 = Hex (128 + (Len(sLength) / 2)) & sLength
    else
        ComputeASN1 = sLength
    End If
End Function

So now we have generated a .req and a .asn file we can copy the .asn to the CA.  We actually used the .inf file created from this VBS script, but the contents look similar enough.

certutil -setextension <RequestID> 2.5.29.17 1 @<dcname>.asn

What Microsoft also doesn’t tell you here, is that if you are using Windows Server 2008, this command will not work unless you have loaded the command prompt with elevated priviledges (UAC). The subject alternative name, which is identified by the object identifier 2.5.29.17, is set with the attributes that are defined in the <dcname>.asn file. The fourth parameter that is set to “1” marks the extension as critical.

The TechNet ‘Processing Domain Controller Certificates‘ article mentions how to validate the certificate request is good, however we are assuming these tests come back as positive so next we need to issue and retrieve the certificate.

Retrieve the Certificate

From the CA now run

certutil –resubmit <RequestID>

and then finally

CERTREQ -retrieve <RequestID> <dcname>.cer <dcname>.p7b

You will now have a .cer and a .p7b file which you can copy back to the domain controller.

Install the Certificate

To install the certificate, from the command line on the domain controller

CERTREQ -ACCEPT <dcname>.p7b

Microsoft tells you to restart the Domain Controller.  In our experience this was not necessary and SSL over LDAP was available immediately

Test LDAP over SSL

After a certificate is installed, follow these steps to verify that LDAPS is enabled:

  1. Start the Active Directory Administration Tool (Ldp.exe).
  2. On the Connection menu, click Connect.
  3. Type the name of the domain controller to which you want to connect.
  4. Type 636 as the port number.
  5. Click OK

Update:  I’ve just updated the adLDAP Wiki documentation to reflect this.

Pirate Bay founders genius idea

May 11th, 2009

Found via Dizzy

The Pirate Bay founders have come up with an genius idea to get their 30million SEK fine paid, which they’ve said they won’t pay.

The plan involves all internet users to pay a small, tiny amount to the law firm that prosecuted them and represnted the music industry..  Basically everyone sends an electronic transfer to their bank account, suggested 1 SEK (which is about $0.13 USD).  Apparently the law firm’s bank account is only allowed 1000 electronic transactions before it starts to cost them, the account holder money.

The best part is that after that 1000 transactions, the fee for each transaction is… 2 SEK! Therefore after those 1000  SEK, if people send just 1 SEK it will cost the law firm more than the money they receive to process, yet in the process no-one will have broken the law!

How to send a message to an already running .NET application

May 7th, 2009

Here is a problem I came across over a year ago now and never got around to detailing.  Here is the scenario

Scenario

I want to create a URL handler in Windows and send that message to my already running .NET application.  For example I create a url handler called myapp: and I create a link in HTML to myapp://www.richardhyland.com/, I want to send that URL to my application, let’s call it MyApp.exe and do something with it.

Now I can quite easily create a URL handler in the Windows registry and call MyApp.exe, but that will create a new instance of the application, what I want  to do is detect to see if MyApp.exe is already running.  If it is then send the URL to that process and if it isn’t, load MyApp.exe.

My Solution

After many hours with Google, I discovered this cannot be done using managed code, so I had to delve into the scary arena of unmanaged code in .NET.

I first set up MyApp.exe to recieve the message.

protected override void WndProc(ref System.Windows.Forms.Message m)
{
  if (m.Msg == 0x400)
  {
    // We've recieved a message from handler.exe (we must now decode the IntPtr and deal with it)
    try
    {
      BS.BuildString(m.LParam);
    }
    catch
    {
    }
  }
  base.WndProc(ref m);
}

void BS_StringOK(string Result)
{
  if (Result.StartsWith("myapp://"))
  {
    Result = Result.Substring(8);
  }
  string strUrl = "http://" + Result.ToString();

  this.funcDoSomething(strUrl);
}

What we’ve done here is override the WndProc function in the application, examined it for a string and passed it to a function called funcDoSomething()

Next we can create a simple, small .exe for the URL handler to open.

Inside the main class of the handler.exe we need to declare

[DllImport("user32.dll")]
private static extern
bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern
bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

[DllImport("user32.dll")]
private static extern
bool IsIconic(IntPtr hWnd);

private const int SW_HIDE = 0;
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_RESTORE = 9;
private const int SW_SHOWDEFAULT = 10;

[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
   public int dwData;
   public int cbData;
   public int lpData;
}

Now you can get the string as an arguement in your form loader and process store it to a string

Now let’s detect to see if MyApp is running

Process[] myProcesses = Process.GetProcessesByName("MyApp");

if (myProcesses.Length >= 1) {
 int n = 0;        // assume the other process is at index 0
 // get the window handle
 IntPtr hWnd = myProcesses[n].MainWindowHandle;

 // if iconic, we need to restore the window
 if (IsIconic(hWnd))
 {
   ShowWindowAsync(hWnd, SW_RESTORE);
 }

 // bring it to the foreground
 SetForegroundWindow(hWnd);

 // Process the message through my builder DLL, see later
 RH.SendMessage.BuildString BS = new RH.SendMessage.BuildString();
 BS.PostString(hWnd, 0x400, 0, strUrl);

 // Message sent to exit
 Application.Exit();
}
else
{
 // Application not running so, start it
 string exeDir = System.Reflection.Assembly.GetExecutingAssembly().Location.Replace("\\handler.exe", "");
 System.Diagnostics.Process.Start(exeDir + "\\MyApp.exe", strURL);
 Application.Exit();
}

Finally we need to detail the String Parser DLL that is mentioned in the above code. This I found online, and it is in Visual Basic

Imports System.Text

Public Class BuildString

    Private Declare Function PostMessage Lib "user32.dll" _
Alias "PostMessageA" (ByVal HWnd As IntPtr, ByVal WMsg As _
Integer, ByVal WParam As Integer, ByVal LParam As Integer) _
As Integer
    Public Event StringOK(ByVal Result As String)
    Private HWnd As IntPtr
    Private WMsg As Integer = 0
    Private WParam As Integer = 0
    Private LParam As String = ""
    Private tempA(-1) As Byte
    Private enc As Encoding = Encoding.UTF8

    Public Property Encode() As Encoding
        Get
            Return enc
        End Get
        Set(ByVal value As Encoding)
            enc = value
        End Set
    End Property

    Public Sub BuildString(ByVal b As IntPtr)
        If b <> 0 Then
            'build temp array
            Dim tempB(tempA.Length) As Byte
            tempA.CopyTo(tempB, 0)
            tempB(tempA.Length) = b
            ReDim tempA(tempB.Length - 1)
            tempB.CopyTo(tempA, 0)
        Else
            'decode byte array to string
            Dim s As String
            If enc Is Encoding.UTF8 Then
                s = Encoding.UTF8.GetString(tempA)
            ElseIf enc Is Encoding.Unicode Then
                s = Encoding.Unicode.GetString(tempA)
            ElseIf enc Is Encoding.ASCII Then
                s = Encoding.ASCII.GetString(tempA)
            Else
                s = Encoding.Default.GetString(tempA)
            End If
            'send out result string via event
            RaiseEvent StringOK(s)
            ReDim tempA(-1)
        End If
    End Sub

    Public Sub PostString(ByVal HWnd As IntPtr, ByVal WMsg _
As Integer, ByVal WParam As Integer, ByVal LParam As String)
        Me.HWnd = HWnd
        Me.WMsg = WMsg
        Me.WParam = WParam
        Me.LParam = LParam
        'create a new thread to post window message
        Dim t As Threading.Thread
        t = New Threading.Thread(AddressOf SendString)
        t.Start()
    End Sub

    Private Sub SendString()
        'create byte array
        Dim ba() As Byte
        'encode string to byte array
        If enc Is Encoding.UTF8 Then
            ba = Encoding.UTF8.GetBytes(lParam)
        ElseIf enc Is Encoding.Unicode Then
            ba = Encoding.Unicode.GetBytes(lParam)
        ElseIf enc Is Encoding.ASCII Then
            ba = Encoding.ASCII.GetBytes(lParam)
        Else
            ba = Encoding.Default.GetBytes(lParam)
        End If
        Dim i As Integer
        For i = 0 To ba.Length - 1
            'start post message
            PostMessage(hwnd, wMsg, wParam, ba(i))
        Next
        'post a terminator message to destination window
        PostMessage(hwnd, wMsg, wParam, 0)
    End Sub
End Class

And voila, that is now I sent a message from one running .NET application to another without starting a new process of the recieving .NET application.

8 bit characters in Active Directory

May 6th, 2009

Once again, after discovering very little information from Microsoft about Active Directory over LDAP I was posed with a question on the adLDAP forums https://sourceforge.net/forum/?group_id=104193

And that was how to handle accented characters over LDAP. Running a standard ldap_modify() will cause a ‘Constraint violation’ error from the domain controller.

I finally discovered, after reading the RFC for LDAP that I need to encode the ‘offending’ characters as UTF-8.

I could have simply executed

$adldap->user_modify('AD.UserName', array('firstname'=>utf8_encode('Göran'));

but that would be too easy wouldn’t it. Seeing as my believe with libraries should be, you shouldn’t have to think about this, let us allow the library to do this work for us.

Whenever you modify or create attributes in adLDAP it passes the attributes array through a schema function so let’s process the array detecting 8 bit characters

array_walk($attributes, array($this, 'encode8bit'));

Then let us create the function encode8bit

protected function encode8bit(&$item, $key) {
        $encode = false;
        if (is_string($item) === true) {
            for ($i = 0; $i < strlen($item); $i++)
            {
                // Detect the ordinal value of the character
                if (ord($item[$i]) >> 7) {
                    $encode = true;
                }

            }
        }
        if ($encode === true) {
            $item = utf8_encode($item);
        }
    }   

Update Wordpress was messing with the rendering of this function, I’ve now corrected it.

I’ve committed the code to the repository trunk and it’s now available to download directly from SVN

adLDAP version 3.0

May 1st, 2009

adldapWell since joining the adLDAP project on SourceForge, I’ve just published version 3.0.

Version 3.0 is a big change from 2.0, firstly I’ve removed support for PHP 4, by using proper PHP 5 constructors and making variables / functions public and protected.

I’ve added a number of new functions for Exchange mailbox creation, contact management and user deletion (as well as PHP Doc style commenting the code).

I’ve also gone and re-organised much of the documentation on the Wiki.

Go check it out for yourself http://adldap.sourceforge.net

Active Directory Management over PHP

April 22nd, 2009

Update: I’m now part of the original adLDAP project, so libAD has been withdrawn, look forward to seeing my contributions and Exchange supposed on adLDAP in the near future.

A while ago I started using a library called adLDAP, unfortunately there have been a number of bugs and lack of some features with it.  Couple this with the lack of updates for the past two years, I decided to re-work this library and release it myself.  This library is called libAD.

libAD is a PHP library providing Active Directory authentication and management over LDAP.

It provides intelligent Active Directory integration with PHP. This extends on the original project that has not been updated for some time. It’s aim is to help other developers with getting over the same hurdles that we’ve experienced in getting the whole LDAP SSL Active Directory puzzle working natively on Linux.

This library is not designed to be a complete Active Directory management systems, but give you a set of functions through an API that will allow you to interface successfully with your Active Directory.

Given the varied nature of organisations and sites, adLDAP may not be your complete solution, but it should be a very sound starting point. LDAP isn’t overly friendly on first glance, and it’s a steep learning curve made alot worse when coupled with Microsoft’s seemingly unending army of catches.

The information you can retrieve from Active Directory is as useful as you make it. If you don’t fill out all their account information there’s not really going to be much to query.

libAD is open source software and is released under the GNU General Public License v2. This is a change from the license used under adLDAP which was LGPL.

Update: I’m now part of the original adLDAP project, so libAD has been withdrawn, look forward to seeing my contributions and Exchange supposed on adLDAP in the near future.

Windows Update frustrations

April 19th, 2009

These days I’m a Mac person, but I do still use Windows for work and also occasionally for other bits and pieces, such as games, Windows software or Windows development.

Now today I booted my Mac into Windows Vista under Bootcamp, started a video encoding process that would take between 2 and 3 hours.  Obviously I’m not going to sit in front of my computer watching it encode a movie file, that would just be a monumental waste of time, so I leave it running.

I come back 2 hours later to discover the computer in sleep mode, that’s fine if the encode has finished then it’ll usually go to sleep automatically.  I wake the computer to discover the OS X login screen.

I check the Windows logs to discover that Windows Update has decided not only to download and install new updates but decided that regardless of what the computer is doing at that current time it should reboot itself and loose any work I happened to be working on.

Now I understand for the default settings and need for Windows Update to auto download and install, it helps protect computers from users who don’t, and have no need, to know about what security patches are required.

What I object to is Windows deciding to reboot itself automatically.  I’ve seen the little prompt before saying click me to snooze the reboot, but I wasn’t at my PC to be able to see that prompt.

The Windows Update system should look at the system processes and open applications to determine whether it might be acceptable to display the prompt, if the processor is very busy, there might just be a chance, that something important is going on!  Alternatively, maybe Microsoft can provide a way to applications to send a flag saying ‘Busy here, move along please’ and then remove the flag when they exit.  If they are worried about Viruses setting that flag then maybe restrict it to digitally signed applications only?

I feel Microsoft are intentionally putting features in Windows to piss off IT professionals!  So two hours of wasted processor time thanks to Windows Update.