Friday, June 30, 2006

Linux: My Painless Experience Installing Ubuntu

I had a change of heart last night on my recovery efforts, and decided to wipe out my Windows install in favor of Ubuntu 6.06. I had copied the desktop Live CD in anticipation of my recovery, and so I decided to give it a whirl. I have to say, I am impressed and really feel that this is what Desktop Linux should be. I apologize beforehand for the lack of screenshots, VMWare was not cooperating with me, and I had no way to capture screenshots off my laptop during the installation process.

Installation:
     The installation kind of threw me for a loop. Its is a different paradigm than what I am used to with OS installs, where you throw in a CD, go through some sort of wizard or configuration menu, and install the OS. Ubuntu doesn’t do that. Ubuntu follows a “try before you buy” method, where you boot into a full fledged Live CD version of Ubuntu and get to trial it before you install it. This is great if your trying to convince someone of all the virtues of Linux and want them to try it out, they agree, and you install it as is right to their system. No fuss whatsoever. I personally like this paradigm a little more since I have a few potential converts and I would like them to try it out first.

When I first pop in the CD, I am greeted with a startup menu. I am impressed with the inclusion of a software memory tester, and I will keep that handy for future diagnostic use. Choosing the boot option starts the Ubuntu load screen giving you the status of the boot process. This is a little prettier than the nasty status screens, however it is not very useful for diagnostics. I will have to remember to change the option for that.

Once loaded, the user is greeted with the Gnome desktop. There is one icon of particular interest here, which is the Install Icon. This is how you initiate the Install process for Ubuntu. A bit different for an OS install, but I like the idea of a test-drive before the install. Once you click on the Install Icon, the installation wizard starts and asks a series of simple questions. The most difficult one, especially for Grandma, will be the disk usage one. I just selected use entire disk, thus wiping out my previously restored Windows disk image. After copying the files to the hard disk, it asks if I want to continue with the Live CD, or reboot into my newly installed Ubuntu installation. This just puts the Debian and Gentoo installs to shame, but then again, those distros target different audiences.

Post-Installation:
The first thing that I noticed immediately was that every single one of my devices was recognized right out of the box. This impressed me. Even my bizarre, crappy USB Wireless adapter was recognized, and it never was recognized in Fedora Core or Debian previously without recompiling the kernel. The only modification I had to make was to the WEP configuration. I seems I forgot the WEP key I was using, and had to reconfigure the WAP with a new key. I also had to install MP3 support, which is not included out of the box. Instructions for doing so are here and here (Note: with link 2, read that document carefully. It covers how to get all the “Restricted” formats from the Ubuntu package repositories called “Universe” and “Multiverse”). Once done, I was able to plug my Ipod into my laptop and play music directly off of it using the default music player. Another interesting thing to note is the small amount of services that start with it. I am used to Debian and Fedora starting a ridiculous number of services. This was not a problem with Ubuntu. In fact, I only had to stop about 3 unwanted services. Not bad. I am impressed with Ubuntu. My only gripe is using Gnome. I much prefer KDE, so I probably should have installed Kubuntu, which is Ubuntu with KDE. I can change out the desktop managers via Synaptic, however I think I will stick with this for the time being. There are things in Nautilus that I like, so I am going to keep with it for a few months.



Thursday, June 29, 2006

Incident Response: Recovery via Knoppix 5.0.1

So I had a chance to begin the road to recovery from my system infection. The great thing is, Knoppix provides such as convenient way to wipe the drive and restore my disk image, so I am back up to running with nothing more than about an hours worth of inconvenience. Plus, as an added bonus, I now have Knoppix 5.0.1, which is great since the last version I had was in the 3.x series.

Here were my steps to recovery.

  1. Have a previous disk image of the system from a known good time. I keep these images on a removable USB hard disk that I keep just for these emergencies. I periodically update these images (roughly once every 3 months).

  2. Boot into Knoppix.

  3. I actually used my Ipod Video to make the backups of the files that were not in my image. Luckily, Knoppix recognized the Ipod as a removable storage device, so all I needed to do was copy the files over.

  4. Wipe the drive. This step really isn’t necessary, but I did so out of spite to destroy the infected disk image. On some primitive subconscious level, this satisfied me in a way that beating down a delinquent payee must satisfy a bookie. The steps to wipe the drive are to run DD using the following command:”dd if=/dev/zero of=/dev/hda bs=100k count=1000”This won’t wipe the whole drive, but it will wipe out the MBR and partition information and render the disk unbootable.

  5. Restore the disk via the previous disk image. Knoppix likes to mount USB drives as SCSI drives, therefore the designation that the USB drive had when plugged in was SDA1. The drive gets auto-mounted on plug-in. To restore the disk is pretty easy, and I outlined the steps in this article, so there is no need to repeat.

  6. Reboot without Knoppix in the CD Drive and away I go.

So now this whole ordeal is behind me. I must admit a little embarrassment in having gotten the virus to begin with, however my recovery time cost me a total of an hour of sitting and waiting for the disk to re-image, which is time I spent catching up on the episodes of “The Shield” that I rented. And I deny the spammer a zombie machine. Too bad there are millions more out there.

Wednesday, June 28, 2006

OS: FreeDOS Ceases Development

I was a little disappointed to read about the discontinuation of development for FreeDOS. While DOS has been “dead” for a long time now, I still like to go back and get a little nostalgic for, what I consider, the Golden Age of Computing. DOS, while primitive, offered some really interesting and educational features, such as direct device access, that modern operating systems deny users due to privileges. I’ve always felt that DOS made a good stepping-stone to understanding PC functions before moving into the more modern OS arena. It’s also kind of sad when developers give up on their hobby due to lack of interest from the community. Since it has been moved to Sourceforge, perhaps another group will take up the flag and bring this OS into the 1.0 version. But with the Linux fanboys questioning why, and the Windows group laughing at the obsoleteness of DOS, I doubt anyone will.

Tuesday, June 27, 2006

VB: Change String Case

A real simple question came across my way. The question was “How can I change case with a Visual Basic string?”

Visual Basic provides two functions for changing case, Ucase and Lcase. Consider the following example. Here, I am going to traverse through a string, and switch the case of each individual character. In function 1, I am going to use a Byte array and switch the case of each element in the array. In function 2, I will build a temp string using the VB string function MID. The string is in a textbox called Text1.

Public Sub Function_1()
    Dim ary() As Byte
    Dim x As Integer
    
    'Set the array size to equal the size of our string. Use lenb to get the actual byte size
    ReDim ary(LenB(Text1.Text))
    
    'Assign the string to the byte array
    ary = Text1.Text
    
    'For the size of the array, go through each character and change the case
    'Remember, this is a byte array, so it compares using numbers, so use the
    'asc function to get the ascii value of the characters to compare against.
    For x = LBound(ary) To UBound(ary)
        If (ary(x) >= Asc("a")) And (ary(x) <= Asc("z")) Then
            'using the chr and asc function, change to a character, change the case
            'and then switch back to an ASCII number.
            ary(x) = Asc(UCase(Chr(ary(x))))
        Else
            If (ary(x) >= Asc("A")) And (ary(x) <= Asc("Z")) Then
                ary(x) = Asc(LCase(Chr(ary(x))))
            End If
        End If
    Next
    
    'Display the result
    MsgBox ary, vbOKOnly
End Sub

Public Sub Function_2()
    Dim temp As String
    Dim x As Integer
    
    For x = 1 To Len(Text1.Text)
        If (Asc(Mid(Text1.Text, x, 1)) >= Asc("a")) And (Asc(Mid(Text1.Text, x, 1)) <= Asc("z")) Then
            temp = temp & UCase(Mid(Text1.Text, x, 1))
        Else
            If (Asc(Mid(Text1.Text, x, 1)) >= Asc("A")) And (Asc(Mid(Text1.Text, x, 1)) <= Asc("Z")) Then
                temp = temp & LCase(Mid(Text1.Text, x, 1))
            Else
                temp = temp & Mid(Text1.Text, x, 1)
            End If
        End If
    Next
    
    MsgBox temp, vbOKOnly
End Sub

Incident Reponse: I Got a Virus

Sometimes I deserve a big kick in the arse. Last night while browsing, I zigged when I should have zagged and caught a nasty little virus. I wasn’t browsing porn sites, if that’s what your thinking, which is too bad, at least I would have gotten some eye candy for my trouble. But I am not too upset, I have been looking for a good excuse to re-format this machine. The virus/virii have all the same staples of recent malware, such as annoying pop-ups, and lame attempts to put things into the Windows startup, and really lame attempts to try to convince me that they are, in fact, virus scanners warning me of an infection. I didn’t check outbound traffic, but I’m sure there’s some attempts to spam. Virus scanner failed to pick it up and AdAware is not finding it in scans, and removing the startup items didn’t stop them from reappearing. Plus, really strange DLL files appeared and are loaded from startup. Attempting to delete the DLLs failed, even from safe mode, or they too re-appeared. Oh well, it happens. I could probably remove it with some persistence, however I am chalking it up as a compromise. With no faith in the system it is time to implement the Incident Response plan. Fortunately this is a non-critical system, so I’ve isolated it from phoning home and from infecting any further systems, or from being a pain and spamming. Reformatting and re-installing is the only viable option at this point. The plan is as follows:

-Boot from a trusted media to backup a few key files that are not already backed up, such as a VMWare DOS virtual, photos of my trip to Vegas and some COBOL source code
-Wipe the drive
-Reinstall an OS (either Windows or Ubantu, haven’t decided). Fortunately I do have a trusted disk image, so it will only require a good ‘ole DD of the disk from that image.
-Restore backups except for the photos. Those will need to be verified individually to insure they have not been tampered with by the infection.

The lesson here, besides to practice safe browsing habits is in the event of a compromise, trust cannot be re-obtained, so be sure to have a good Incident Response Plan.

Monday, June 26, 2006

Oracle: Oracle Uninstall

Sometimes working with Oracle can be a big pain in the backside, especially when trying to uninstall and ensuring that all components have been removed. Case in point, we had an employee whose desktop application failed to connect to the backend database correctly after having her machine reformatted and “re-imaged”. The image file came pre-installed with an Oracle 8.0.5 client. Kind of made things difficult to connect to an Oracle 9i Server. Rather than playing around with the TSNNAMES.ORA file to resolve this, I opted to upgrade the system in question to Oracle 9i. This is where the “Help Desk” failed. First, since they couldn’t find the Uninstall option in Add/Remove programs, they didn’t uninstall the old version. Then, they just applied another “image” of Oracle 9, which was corrupt. They didn’t bother removing the old Oracle from the PATH statement, set the 9i version in the HOME setting, so of course, this didn’t work. The system could not connect through ODBC, testing through a UDL file, or through the desktop client. Sql-Plus worked, but only with version 9. Luckily I was able to get this resolved. Here are the steps I followed.


  • Uninstalled version 8.0.5 via the Oracle Universal Installer.

  • Uninstalled the Corrupt 9i via the Universal Installer.

  • Removed the old Oracle 8.0.5 entry from the path statement (C:\orant\bin).

  • Backed up and deleted the C:\OraNT directory.

  • Backed up and deleted the C:\Oracle directory.

  • Removed the following registry keys (Note, * are wildcards):

  • Hkcu/Software/Oracle

  • Hklm/Software/Oracle

  • Hklm/Software/odbc/Microsoft/ODBC for Oracle

  • Hklm/software/odbc/odbcinst.ini/Oracle*

  • Hkey/Classes/root/(ora*, orcl*, oradc*, orammc*, oraole*, oraperf*)

  • Hkusers/software/oracle

  • Hkusers/s-(long number here)/Software/Oracle

  • Hklm/SYSTEM/ControlSet00*/ Services/ (Oracle*)

  • Hklm/SYSTEM/ CurrentControlSet/ Services/ (Oracle*)

  • Reinstalled Oracle 9i Client fresh from CD.

And that resolved the connectivity issues.

Thursday, June 22, 2006

Programming: XML-RPC Example

Thanks to OSNews.com for posting this IBM Developerworks Article about XML-RPC. Web Services are an interesting topic, and there are a few standards out there for implementing them. This article describes a program that exposes a simple “Add” method in C++ as a web service. Neat stuff.

Wednesday, June 21, 2006

Programming: Don't Program When Programming isn't Required

My friend Richard Bejtlich recently asked for help with a website redesign. Considering my ever-low opinion of programmers who are under the assumption that they are web designers, I offered my usual advice of “hire a professional”. In a case like his, the web site he has provides information on his business, so offering goofy JavaScript or database interactions seems a little outside of his scope. Since this is basically an electronic brochure, I recommended that he find a graphic artist. Not every project requires programming, and adding a PHP backend or unnecessary additions can introduce unexpected results (IE: it would be embarrassing for a security consultant to have his website hacked).

Tuesday, June 13, 2006

Excel: Creating Static SQL Insert Statements Via Macro

There are times that I don’t particularly need to insert data from a spreadsheet right into a database, but would rather have a script generated or a series of SQL statements generated from spreadsheet data. I usually do this in cases where I have a set population sent to me via spreadsheet, and I am creating a script to initialize tables. This way, in the even of a crash or if I need to set up a QA/UA/Testing environment, I can simply run the script and have my initial tables and sample data ready to go. This works out great when I have a set population for an online report.

The easiest way to do this from Excel is using the debug.print statement, which will output text directly to the Immediate window. This has some drawbacks, since the immediate window can only hold so much data. If the script that is generated is large, I can output directly to a file, or into another sheet. Below is a sample macro that will generate the insert statements I need based on the sample form in Figure 1 and display the results into the immediate window. This can easily be modified if needed to output to a file or to another sheet inside of a workbook.


Figure 1. The Form

Option Explicit

Private Sub cmdAddCol_Click()
'Add if there is something in the textbox
If txtColumn.Text <> "" Then
lstCols.AddItem txtColumn.Text
End If

'Clear and set focus back to the textbox
txtColumn.Text = ""
txtColumn.SetFocus
End Sub

Private Sub cmdAddField_Click()
'Add if there is something in the textbox
If txtField.Text <> "" Then
lstFields.AddItem txtField.Text
End If

'Clear and set focus back to the textbox
txtField.Text = ""
txtField.SetFocus
End Sub

Private Sub cmdClose_Click()
'Close the form
Unload Me
End Sub

Private Sub cmdCreate_Click()
Dim sql_prefix, sql, sql_suffix As String
Dim LastRow, x, y As Long
Dim temp As Variant

'Get the number of cells. If there are 0, exit this function
If WorksheetFunction.CountA(Cells) > 0 Then
LastRow = Cells.Find(What:="*", After:=[A1], _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
Else
MsgBox "There are no cells in this sheet!"
Exit Sub
End If

If lstCols.ListCount <> lstFields.ListCount Then
MsgBox "Field count does not match Column Count"
Exit Sub
End If

'Create out prefix and suffix
sql_suffix = ");"
sql_prefix = "insert into " & txtTable.Text & " ("

''Add the fields to the prefix
For x = 0 To (lstFields.ListCount - 1)
sql_prefix = sql_prefix & lstFields.List(x) & ","
Next

'Remove the extra comma and add the remainder of the prefix
sql_prefix = Left(sql_prefix, Len(sql_prefix) - 1)
sql_prefix = sql_prefix & ") values ("

'Now, create the actual queries to use for each row in the sheet
For x = 1 To LastRow
'Preset SQL to equal the prefix, clearing the value from the last loop
sql = sql_prefix

'For each column listed in the list box
For y = 0 To (lstCols.ListCount - 1)
sql = sql & "'" & Range(Trim(lstCols.List(y)) & Trim(Val(x))).Value & "',"
Next

'Remove the extra comma and append the suffix. Then print the results
'to the immediate window
sql = Left(sql, Len(sql) - 1)
sql = sql & sql_suffix
Debug.Print sql
Next
End Sub

Now, all I need to do is fill out the form, and the SQL statements will be generated for me. I can use this to generate my initial scripts based on a user population.

Monday, June 12, 2006

A Simple AJAX Report Form

I have always been a big supporter of desktop applications. I have always found the trend to try and throw everything into a web application a questionable and ridiculous attempt to capitalize. This is especially true for transactional applications, such as banking software. In my opinion, the overhead more that outweighed the benefits in. Due to that line of thinking, I have been incredibly hesitant to try and accept AJAX. However, I must admit, after actually diving into it and trying a few simple pages, I can see where it can help to bridge the gap between my coveted desktop applications and the slow, kludgy web apps I have come to expect.

This article is in no way a tutorial on AJAX. There are plenty of excellent articles on AJAX programming, books, and all sorts of information to help someone get their feet wet. I recommend this IBM Developerworks Series. This article will simply highlight my experience with completing the form I discussed last article.

The goal of the form is to have it call a BIRT report and display the results in the form page itself, rather than making a page switch or display the resulting page in a frameset, which I have always found to be ugly. The parameters that will be passed into my BIRT report come from two form fields, which are populated using the calendar component mentioned in my last article.

Here are the JavaScript snippets used in the code.

<script language="javascript" type="text/javascript">

/* Create a new XMLHttpRequest object to talk to the Web server */
var xmlHttp = false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
  xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
  try {
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (e2) {
    xmlHttp = false;
  }
}
@end @*/

if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
  xmlHttp = new XMLHttpRequest();
}
</script>

This is borrowed directly from the IBM developerworks page. This will create a new XMLHttpRequest object, regardless of which browser family is used (IE or Standard, I have only tested with Firefox, IE, and Mozilla).

function callServer() {
  // Get the start and end date from the web form
  var start_date = document.getElementById("start_date").value;
  var end_date = document.getElementById("end_date").value;
  var cd_loc = document.getElementById("cd_loc").value;

  // Only go on if there are values for both fields
  if ((start_date == null) || (start_date == "")) return;
  if ((end_date == null) || (end_date == "")) return;

  // Build the URL to connect to
  var url = "http://localhost:8080/birt_viewer/run?__report=RoomUtilizationReport.rptdesign&Start_Date=" + escape(start_date) + "&End_Date=" + escape(end_date) + "&LocationCode=" + escape(cd_loc);
  
  // Open a connection to the server
  xmlHttp.open("GET", url, true);

  // Setup a function for the server to run when it's done
  xmlHttp.onreadystatechange = updatePage;

  // Send the request
  xmlHttp.send(null);
};

This function will make the call BIRT for my report using the xmlHTTPRequest object. First, we get the parameters by using the getElementByID functions for each of the following form fields, the start and end date which specify a date range to search in the report, and the cd_loc field, which specifies a location code in the report. This doesn’t matter since the report isn’t really the heart of this article. Next we make sure the required fields are not null. Then we build the BIRT Report Server URL to call to retrieve the report. Next, we setup the xmlHTTPRequest object using our constructed URL, and setup the callback function to the updatePage function, which I will discuss next. Then we send the request to the server. This will happen in the “background” of the page and the user will be none the wiser.

function updatePage() {
     //If we have recieved a response
     if (xmlHttp.readyState == 4)
     {
          var imageElement = document.getElementsByTagName("processingIcon")[0];
          //If the parent node exists and is not null, process this
          if (imageElement)
          {
               var parentElement = imageElement.parentNode;
               parentElement.removeChild(imageElement);
          }
          //Create the element to contain our returned HTML page from BIRT
          var childElement = document.createElement("report");

          //Set the innerHTML code to the returned report page
          childElement.innerHTML = xmlHttp.responseText;

          //Go through the DOM tree until we find the body element
          var bodyElement = document.getElementsByTagName("body")[0];
                         
          //Find and remove the old section, if it exists
          if (bodyElement.hasChildNodes()) {
               for (i=0; i<bodyElement.childNodes.length; i++) {
                          var oldElement = bodyElement.childNodes[i];
                         if (oldElement.nodeName.toLowerCase() == "report") {
                         bodyElement.removeChild(oldElement);
                         i--;     
                         }
               }
          }
          
          //Append our Report result intot he HTML body
          bodyElement.appendChild(childElement);
     }
     else
     {
          //Display a animated gif to give the impresion of a processing bar
          var childElement = document.getElementsByTagName("processingIcon")[0];
          
          if (!childElement)
          {
               var childElement = document.createElement("processingIcon");

               childElement.innerHTML = '<img src="images/animation_hourglass.gif">';

               var bodyElement = document.getElementsByTagName("body")[0];
               var reportElement = document.getElementsByTagName("report")[0];

               if (reportElement)
               {
                    bodyElement.insertBefore(childElement, reportElement);
               }
               else
                    bodyElement.appendChild(childElement);
          }
     }
};

This is the most complex part of the page. The basic logic flows like this:
     If response. readyState from xmlHTTPRequest is == 4 then
          -Remove the processing icon if present
-Create new page element called report, and put result from request into this new section
-Remove all old report sections so there are no duplicate reports in the same page
-Add newly created section to the page
     else
          -Check to see if processing Icon is present
-If no processing Icon is present, create a new section and display an animated Icon for the user to look at, giving the impression of processing the request. If there is an already present report section, insert before this. Otherwise, just put at the end of the page.
     
To check the readyState of the xmlHTTPRequest object, the following code is used:

          xmlHttp.readyState == 4

For reference, the readyState codes are as follows (as borrowed from here):
0 = uninitialized
1 = loading
2 = loaded
3 = interactive     
4 = complete
As far as the processing Icon, I could have put in a branch if the readyState was equal to 1, but I chose the lazier path. To create the Processing Icon, first I checked if the section containing it exists using the following code:
var childElement = document.getElementsByTagName("processingIcon")[0];
          
     if (!childElement)

This way, if childElement does not exist, then the element does not exist on the page. The reaction of the script to display the processing icon is based off of this. I am sure some JavaScript guru can tell me a better way to do this, however this works fine for me.

The rest should be self explanatory, or at least understandable if you read the IBM Developerworks articles.

A few things became pretty apparent while working with AJAX. First, AJAX not the ultimate answer to all the webs problems. While it is a cool architecture, AJAX alone lacks structure, and it lacks a formal framework. AJAX also has the potential to suffer from abuse, which is something I hate to break to the fanboys. Picture this, you’re the dirt-bag spammer/online advertiser. Pop-ups and cookies are no longer an option for you to trap personal information from browsers. So what do you do? AJAX is new, and no subject to suspicion just yet, so you create a simple function that posts the browsers information via an AJAX call, and since this is done Asynchronously, it is done without the users knowledge or consent. This is of course assuming that this is not already being done. AJAX also suffers from the inability to design itself. While it is a powerful tool, its usage can be less than spectacular if the design of the site is poor.

Wednesday, June 07, 2006

Simple JavaScript Code to Hide DIV Tag

Recently I was working on a web-based report for a project I was working on. Since I typically do not do web development, I wanted to do some work with the AJAX and the DOM. I will cover the AJAX part in a separate article. Granted, DOM and JavaScript manipulation is anything new or exciting anymore, however I don’t do much in it, so it was a welcome change of pace.

The requirements were fairly simple since this was just an experiment on my part. I wanted the form to submit via an AJAX call to a BIRT report server, display in the same page, and have the ability to remove the form, all using the DOM to manipulate the appearance on the page.

The form itself has two date input components. Rather than reinvent the wheel, I leveraged an existing HTML calendar component from here. This did everything I needed and more. Instructions for setting it up are available in the package itself.

As far as hiding the form, created a DIV tag surrounding the form element and labeled it reportForm. I then used the following function to remove the form:

function hideForm()
{
     var formElement = document.getElementById("reportForm");
     var parentElement = formElement.parentNode;

     parentElement.removeChild(formElement);
};

I assigned this function to a HTML Button component on the onClick event. In the function I could have given the form a name, and used the function documents. GetElementsByTagName, however if there is more that I want to hide than just the form, a DIV tag will group these together, so this was the method that I chose.

This in itself, isn’t really exciting. However, when I cover the AJAX call, that will be a little bit more interesting.