Thursday, December 18, 2008

SqlDbx - Really Cool

I was asked to work in a project migrating from classic ASP to .Net and the database used was Sybase ASE. The only option for DB coding, was to use the pre-installed SQLAdvantage.

The first thing I did was to search for a better tool and after getting SqlDbx I knew, I've got the best.

Guys in my team were thrilled at this product. I have recommended this one to my company's IT Tools department and I am sure each one of us would get the professional edition real soon.

Wednesday, December 17, 2008

Building a simple Dirty State Page in .Net with AJAX

I was building a small internal application for my company as a pilot application that tests the Business Object framework we had just built. This web application could well serve as a useful too.

And now I wanted to try out a simple dirty state page – a page that tracks any changes made to it and notifies accordingly to the user. With AJAX extensions, I know it was possible, there are options to extend and create your own control - like a DirtyStateUpdatePanel or something. Anyways, I just wanted to try a basic version - so here is the simple version that I came up with. Consider this as a work done for a proof of concept.

DISPLAY
We need something to notify that the page has become dirty – even a div tag should be suffice
<div id="DirtyStateNotifyDivCtl" class="dirtyPanel" runat="server">div>

CONTROLS AND EVENTS
We need to notify if any of the bound controls have changed – we should be fine with each control’s changed event. Additionally we need them all to perform one common function – notify a dirty state change. So we can have our own event declared

Public Event DirtyStateChangedEvent(ByVal getDirtyStateMethod As IDirtyPage.GetDirtyState) Implements IDirtyPage.DirtyStateChangedEvent

And its handler:

    Protected Sub ValueChanged(ByVal getDirtySateMethod As IDirtyPage.GetDirtyState) Handles Me.DirtyStateChangedEvent

        With Me.DirtyStateNotifyDivCtl

            If getDirtySateMethod.Invoke() Then

                .InnerText = "*Unsaved Changes"

            Else

                .InnerText = " "

            End If

        End With

    End Sub

And a cheeky way to expose the raise event from Master Page:

    Public Sub RaiseDirtyStateEvent(ByVal getDirtyStateMethod As IDirtyPage.GetDirtyState)

        RaiseEvent DirtyStateChangedEvent(getDirtyStateMethod)

    End Sub

POSTBACK
We need to get all our controls wrapped into an UpdatePanel to “Ajaxify” things.

DETERMINE IF DIRTY
We can wire up the page controls to one common event handler (you can have as many event handlers). And pass in a delegate of a GetDirtyState method and let the Master page take care of the display of the Unsaved change text – or clear the unsaved change text.

    Protected Sub Page_DirtyStateChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles EndDateTxtCtl.TextChanged, StartDateTxtCtl.TextChanged

        Me.MasterPage.RaiseDirtyStateEvent(AddressOf Me.GetDirtyState)

    End Sub

 

    Public Function GetDirtyState() As Boolean

        ' TODOK: loop through your controls and find out if the page has become dirty by any chance

        If Me.EndDateTxtCtl.Text <> Me.TeamProject.EndDate Then

            Return True

        Else

            Return False

        End If

 

    End Function

And the icing on the cake would be the following:
<script language="javascript" type="text/javascript">

window.onbeforeunload = confirmIfDirty;

function confirmIfDirty(){

    if(document.getElementById('ctl00_DirtyStateUpdatePanelCtl').innerText > "")    {

        event.returnValue = "There are some unsaved changes. Navigating away from this page would cause such data to be lost.";

    }

}

script>

The slick onbeforeunload event pops up every time the user tries to navigate away from the page leaving it dirty.

You can find the sample files here.

 

Questions… Projects… - my two cents

A few years back, I happened to hear a glorified programmer advising his self-proclaimed sidekick how important it is to ask lesser questions to technical stakeholders/business users about the project or technology that they are working on.

I couldn’t disagree more! He was arguing that one should ask “smart” questions and should never let the other person think we do not know anything about the business by asking "stupid" questions.

Let me put this straight - There is nothing called as a stupid question. People get annoyed only when they get repeat questions. It would make them nervous on whether you are really on top of things.

For the benefit of the developer and for the benefit of the project, one should ask as many questions as possible. I am sure that clients who want the project done successfully would love to give you the answers. Even if they don’t have it with them immediately, it would kick start the thinking process and will make them revisit the requirements.

It helps to clarify a lot of things and verifies your assumptions. I strongly believe in this – “Assumption is the mother of all mess-ups”. It is wise to put forward your assumptions and sort it out. Nothing wrong in asking something basic about the business, after all you are going to be held responsible for the project you are build, aren’t you? The client would also be expecting you to know what you are doing.

Asking a lot of questions make you smart.
Asking only smart questions could leave even you in a lot of doubt.

Sybase stored procedure bug!?! Due to renamed table

We were investigating an issue with a web page – that displays data from a Sybase database. The problem was simple – the data was not as expected. The values were missing.

When we queried the actual table (say TableA) from which the stored procedure selects, the information was there. But on running the stored procedure, there would be no results.

We first suspected the parameters that were being passed in – but they were fine.

We then ran the stored procedure with IO statistics On and traced the culprit. The stored procedure was selecting records from a totally different table (TableA_old). The logical explanation is this:

Somebody renamed TableA to TableA_old. Thereby breaking all stored procedures, views depending on it. Later this somebody made another table by the same name TableA. But that does not fix the things that were broken earlier by the renaming.

The stored procedure (which is precompiled) was still pointing to the old table since it was referencing the table using id’s used internally by the database.

We saw if there was an option to recompile as I remembered that there was a sp_recompile, but that did not help. I played around with sp_recompile and it did mark the table to cause all associated sp’s to be recompiled the next time it was run. But when I ran the stored procedure again, it recompiled (there were 2 IO Stats entry), but both were pointing to the old (renamed) table only.

We then dropped and re-created the stored procedure and that fixed it all.

Tthe Sybase version I was working on was quite an old one:
“Adaptive Server Enterprise/12.5.3/EBF”

Here is a quick way to find out what version you are in. SELECT @@version
To just give some code examples to reproduce this error, try this on some old version of Sybase:

CREATE TABLE tbl1
(
name VARCHAR(10)
)

CREATE PROCEDURE sp_tbl1
AS
SELECT name FROM tbl1

SET STATISTICS IO ON
EXEC sp_tbl1

/*
**RESULT WOULD BE:
Table: tbl1 scan count 1, logical reads: (regular=1 apf=0 total=1),
physical reads: (regular=1 apf=0 total=1), apf IOs used=0
**/

SET STATISTICS IO OFF
sp_rename 'tbl1','tbl1_old'

CREATE TABLE tbl1
(
name VARCHAR(10)
)

SET STATISTICS IO ON
EXEC sp_tbl1
/*
**RESULT WOULD BE:
Table: tbl1_old scan count 1, logical reads: (regular=1 apf=0 total=1),
physical reads: (regular=0 apf=0 total=0), apf IOs used=0
**/

DROP PROCEDURE sp_tbl1
DROP TABLE tbl1
DROP TABLE tbl1_old

Sunday, December 14, 2008

How to extract contents from .msi files - Windows XP

My company’s strange “security” policy granted me administrator rights ONLY on my PC, but did not add me up to the administrators group of the domain to which my user account belonged to. That gave me kind of mixed permissions – and often caused some annoying “You do not have permissions” error. One such thing was - running an .msi file. I was installing AJAX Extensions when this one popped up again.

I have done some workarounds before for normal executables (.exe), with my handy WinRar, which would extract all contents of a setup package. But I was dealing with this Microsoft Installer, which was a bit tricky. But surfing through MSDN helps, as always! Learnt how to do it the black & white way:

Run the following on command prompt:
msiexec /a “the msi file with path” /qb TARGETDIR=”the path where you want the contents to be extracted”

Example:
msiexec /a “D:\Installers\AJAXExtensions.msi” /qb TARGETDIR=”C:\AjaxExtensionsExtracted”

Since I was an administrator on my local machine, I knew I had the rights to run the contents from the extracted folder. Well, at least I did not sweat much on it.

Release Process !?!

Two days back, my teammate had to release to production a minor code change he made to one of the web modules. Since we both were new to the release process followed by the client, we were given a small briefing about it.

We first have to release the change to a common dev environment, where it will reside till the peer review and internal testing is completed. Then we had 3 documents to be meticulously filled up for the UAT (User Acceptance Testing). One of the documents gives a clear step by step instruction on how to release to the QA environment, explaining like it is being told to a 4 year old. Once the UAT release is done, the development team we would get an email notification and after the user test it, there would be yet another email notification giving a sign-off for a production release.

We then have to prepare another bunch of documents, add with that the UAT sign-off email and the UAT release notification email and send it across to the deployment and maintenance team at the client side, who would take care of the release. They follow a semi-automated release process whereby the release manager would just specify the steps to be executed and the scheduled deployment runs once every 3 hours.

On the release date, if you never hear back from the release manager, things are good. But if you get an email with a high alert… you probably woke up on the wrong side of the bed that day.
Well, apparently that’s what my teammate would have possibly done, because he had just received such an email. He had a senior colleague to help him figure out the problem. So I decided to keep away from it, after just telling to compare the code changes in source control and holler if he needs any assistance. I decided to stay out of it partly because I did not want to jeopardize the senior colleague’s approach and also because I myself had a couple of burning issues, to attend to.

While I was keeping busy, later that evening I heard from this teammate that they had to rollback the changes after trying a couple of things and that this has been escalated and needs to be re-released the next day. They had identified that a few things were not matching up with the QA and Production environment. But they had figured out what the problem was and they were prepared for the next day’s release. While he was explaining I asked a few questions for which he either had no answers or was not confident of them, which made me a bit nervous for him.
Well, it was the next day - same time, and sadly the same story. It was a Friday and people were all the more frustrated and this colleague of mine was being made responsible and he had no clue what was going on. When things were going towards yet another rollback, I decided to jump in, invited or not.

They had this published website which was released to UAT – which worked fine, but when released to production, it failed to even load the default page and redirected to an authentication failed error page. The “fix” after the first rollback was this – they had compared the config file and found some application specific Role-id’s to be different and assumed that this must have been the issue and prepared themselves to give another shot, which eventually failed – miserably.

The code version in the source control looked intact, leaving me no other option but to decompile. Reflector, my favorite tool in many instances came to the rescue and I decompiled the app code binary of the current version in production with ours. When I saw the decompiled login related method, it seemed to contain quite a lot of additional changes – changes that were not present in source control!

We then figured out a way to get this work by making a few changes in the configuration file. Call it a tweak or a hack but it saved the day and the weekend, but I made it aware to all the people involved in this including the client who got an emergency regression test run and also promptly created another request to get this mess cleaned up.

I have worked quite a bit in this onsite/offshore setup and have my own experiences to recon. But almost no client I have worked with before had such an elaborate documentation process. So at first I appreciated this way of working, but this incident clearly proved that no system or process is invincible to errors, not when people have the audacity to bypass certain rules.
This whole thing was obviously caused due to someone who had been here long enough and knew how to sidestep a few landmines, but unfortunately did not know or care about the consequences and complications it would create to the next bunch of people who would work on it. The blame game had started, and something tells me that whoever did this must have been long gone.

Even though I come from a background where documentation was not so much patronized, I still could suggest on maintaining a “deviation log” which can be mainly used to record any such deviations, in any step of the development life cycle.

The bottom line is this - 
Even though there are a lot of steps to streamline any work process – nothing could change unless people learn to respect it and follow it.

Tuesday, December 9, 2008

Adding Properties to an object dynamically in VB.Net

In the current application that I am building, I came across this scenario where I had to add properties to an object during runtime.

Basically it is like this:

I had to display some data of a parent object on a grid – but the number of columns to display depended on the number of child objects the parent had. I usually build a façade object as a standard practice to bind any such modified/tweaked/manipulated data objects.
In order to explain the problem clearer, let me give an example.

Say we have “Product”, “ProductVersion” and “Order”. There could be multiple versions for the product and the relationship is one-to-many. “Order” and “ProductVersion” have a foreign key constraint.

In the middle tier we have Product class and ProductVersion class that map directly to each table. The requirement is to display in grid on the Products page that shows the following –

·         A side by side comparison of each version’s cost breakdown

·         Display is based on the Order Status

Product’s Order Status

Version 1

Version 2

Version 3

Shipped

$1024

$689

$60

Processing

$488

$843

$593

Pending Payment

$734

$1009

$978


One way is to get this whole thing done in a stored procedure. But just to make it happen in the middle tier, I dived into the world of TypeDescriptionProvider, partly because on an earlier project, when we encountered a similar problem, we chickened out of it by forcing the user  to change the functionality ( only due to lack of time).

Anyways, here goes the solution. You can get the actual code in zipped format here.
I am just explaining the most important parts:

The Class that I am going to build to bind with the grid is called – “ProductVersionFaçade”
The properties it will have – OrderStatus, and the rest of the properties would be Dynamically added.
Mark the façade class to use the TypeDescriptionProvider which we are about to build

GetType(ProductVersionFacadeTypeDescriptionProvider))> _

Public Class ProductVersionFacade

Let us have a dictionary inside this to hold our dynamic properties. You will see its usage later.

We then create our TypeDescriptionProvider as follows:

    Public Class ProductVersionFacadeTypeDescriptionProvider

        Inherits TypeDescriptionProvider

And override the GetTypeDescriptor function, which will return a CustmTypeDescriptor, which is also something we are going to build:

    Public Class ProductVersionFacadeTypeDescriptor

        Inherits CustomTypeDescriptor

This is the class where the most important GetProperties function will be overridden and inside that we add our new bunch of properties like this:

With propCollection

    Dim attArr() As Attribute

    ReDim attArr(MyBase.GetAttributes.Count - 1)

    MyBase.GetAttributes.CopyTo(attArr, 0)

 

    If TypeOf (_instance) Is ProductVersionFacade Then

        f = DirectCast(_instance, ProductVersionFacade)

        For Each i As Integer In f.DetailHourDict.Keys

            keyString = String.Format("ProductVersion_id{0}Cost", i)

            .Add(DirectCast(New ProductVersionFacadePropertyDescriptor( _

                    keyString, GetType(Integer), attArr), PropertyDescriptor))

        Next

    End If

 

End With

This is where we add the properties to the instance’s dictionary. And if you note we are adding a custom PropertyTypeDescriptor, we need that too. So we build the ProductVersionFacadePropertyDescriptor which among many other things overrides the GetValue function to return the value for your newly added properties.

Back to the Façade class, we overload the default property of it as follows:

        Default Public Property Item(ByVal fieldName As String) As Object

            Get

                Dim value As Object = Nothing

                If _customPropsDict.ContainsKey(fieldName) Then

                    value = _customPropsDict(fieldName)

                End If

 

                Return value

            End Get

            Set(ByVal value As Object)

                _customPropsDict(fieldName) = value

            End Set

        End Property

And a sample from the Page where it is bound:

Private Sub BindFacades()

 

   Dim list As List(Of ProductVersionFacade)

   list = ProductVersionFacadeSvc.BuildFacades(Me.Product)

 

   Dim keyString As String = String.Empty

   Dim idString As String = String.Empty

 

   For Each f As ProductVersionFacade In list

 

       For Each i As Integer In f.ProductVersionCostDict.Keys

           keyString = String.Format("ProductVersion_id{0}Cost", i)

           idString = String.Format("ProductVersion_id{0}", i)

 

           If f._customPropsDict.ContainsKey(keyString) Then

              f._customPropsDict(keyString) += f.ProductVersionCostDict(i)

           Else

              f._customPropsDict(keyString) = f.ProductVersionCostDict(i)

           End If

           f._customPropsDict(idString) = i

       Next

   Next

   Me.FormatFacadeGrid(Me.ProductVersion)

   With Me.ProductVersionFacadesGridViewCtl

        .DataSource = list

        .DataBind()

   End With

End Sub

Thursday, December 4, 2008

Output Parameter from stored procedures in Sybase ASE

I have never worked with Sybase, until recently – since one of my current employer’s clients is having their data almost fully in Sybase. A really early version of Sybase too.

Now there are a slew of things that you would have to re-adjust to when you are working with Sybase, especially after working extensively and entirely in SQL. Sybase is case sensitive and we are currently given to work with SQL Advantage which is a pretty naïve tool and stands no where close to my favorite Query Analyzer. But of course, it was built for the yesteryears. If there is anyone I should blame, it must be the client for still not upgrading!

So, as and when I learn something, I would try to note it down here.

First thing I learnt –
Problem:
Output parameters on Sybase stored procedures would not return back to your recordset.

Usual workarounds
Will “Select” the value and get it as part of the result set

Actual Fix
Set the CursorLocation property of the Connection object to adUseClient (adovbs.inc value 3), like this:

objConn.CursorLocation = adUseClient

OR

objConn.CursorLocation = 3

Thursday, November 27, 2008

Date Formatting in GridView BoundField

Quick note on this – spent a few minutes on getting this to work. 

Formatting a date field value on a ASP:GridView did not work initially. I had a function that returns a bound field as given below. But No matter what I did, I could not get the formatting of the field to work.

 Say for instance I set the format string as {0:mm-dd-yyyy}. It would still display the field in the default long format. Until I set the "HtmlEncode" to False, which saved me some time:

 

    Public Function GetBoundField( _

              ByVal dataField As String _

            , ByVal headerText As String _

            , ByVal headerStyle As String _

            , ByVal itemStyle As String _

            , ByVal dataFormatString As String _

            ) As BoundField

 

        Dim bf As BoundField = Nothing

 

        bf = New BoundField

        With bf

            .DataField = dataField

            .HeaderText = headerText

            .HeaderStyle.CssClass = headerStyle

            .ItemStyle.CssClass = itemStyle

            .HtmlEncode = False

            .DataFormatString = dataFormatString

        End With

        Return bf

    End Function

Tuesday, November 25, 2008

Search for files containing Text - Windows XP

Have you ever tried to search for files containing certain text? The search criteria where you can enter some text into the textbox labelled - "A word or phrase in the file:"

I had found it very useful before but then I was searching text files maybe. 

When I search the folders where I have my code (.vb, .aspx files etc.), there will be no results. Even when you search for words which you are very sure is present in almost all files. 
(I had an alternative though, I use my Visual Studio find and would specify a folder path to search for, but then in Production and some restricted environments, this functionality would be lost - No Visual Studio to help you see.)

By default the Windows XP search does not consider many file extensions. One common reply you would see on googling this is to add some registry entry for each of these file extensions you have a problem with. But before you try that, check this out:

This method was a lot quicker and easier:

Start-Run-> Type mmc %windir%\system32\ciadv.msc
This is the indexing service console - Select Action-Properties. In the Generation Tab, Inheritable Settings Group - Check the Index files with unknown extensions.(if not already checked.)

Now give it another shot - No restart required.

Sunday, November 23, 2008

iTunes Music Stuttering Issue

Very recently I encountered this problem with iTunes. I was running iTunes v 7.6.0.29. I was syncing my iPod classic 80 Gig fine for almost 6 months, after which I connected my new iPod Shuffle – that is when I noticed the problem. All the songs in my iPod shuffle played like a bad CD with garbled noises and kept skipping all the songs after trying to play it for a couple of seconds.

I then realized that even while playing the songs right from iTunes had the same problem. ALL my songs, which were earlier working fine, now did not, EXCEPT one particular album, which I had ripped myself from the audio CD.

I tried to remember what could have possibly caused this to happen. Maybe it was connecting another iPod device? Maybe not, I have seen people do that before without a problem.

I then remembered that I had installed Winamp just a couple of days before, after which I opened iTunes only to connect my shuffle. Maybe that installation messed up things?

I was unsure. So here are the steps I took.

  1. Tried uninstalling Winamp, did not help.
  2. Pounded the internet for fixes, looked almost everywhere and every post spoke about changing the Quick Time Player properties – tried that but nothing worked. I desperately tried setting different combinations of output rate, sample rate etc. but nothing worked.
  3. People had suggested it could be because of insufficient RAM, since iTunes has a reputation for hogging CPU for its “CoverFlow” view – but that was impossible, Mine had 3 gigs of RAM, besides, the CPU usage when running iTunes was, on an average, only about 3-4%.
  4. I then resorted to System Restore as it was driving me crazy. I tried restoring my system to as early as 3 months back – still NO DIFFERENCE!
  5. Luckily it was a Friday and I had the weekend and I decided to devote as much time to it as it takes. I backed up my songs and fully formatted my disks and got my OS reinstalled.
  6. I then installed all my onboard device drivers and then iTunes and tried playing the songs – STILL DID NOT WORK. I was totally stumped!
  7. With absolutely no other ideas in hand, I aimlessly surfed the net for some clue and stumbled upon one link where the user mentioned that sound card drivers could be a possible issue.
  8. I then searched if any sound card upgrades were available for my Intel motherboard – there was one and I downloaded and upgraded to it.
  9. This time when I tested, I loaded music from a different collection as I childishly felt that the other songs were probably jinxed. But to my surprise it WORKED! All newly added songs played smooth! And when I tried to play the old files, they stuttered. So they were jinxed in some form indeed!
My conclusion was this – the music files that initially played bad in iTunes – is damaged forever. So if you are testing any of the fixes, remember to use new music files each time.

Also I noticed yet another thing – When I tried to play music in the default windows Media Player on my newly installed Windows XP system – almost all of them caused Media Player to raise this warning – The file you are attempting to play has an extension (.MP3) that does not match the file format. That explained a few things. Plenty of my files were badly encoded mp3 files, which regular players like Winamp plays without even a warning.

Some files played fine when I clicked Yes, but some played bad even in WMP! I then took aside the files that played fine in WMP and renamed the extension to .wma and tried playing it in WMP and this time the warning went off and it played fine. So a bunch of files I had were actually .wma files with just their extensions changed to .mp3. No wonder iTunes struggled.

I am glad it works for me now, but I am still inconclusive on what exactly could have caused this problem in the first place. Anyways, I am just posting this for people who might want some ideas to try out. Trust me, I know how frustrating the problem is!

Online Groups/Discussion board ethics

I recently joined this http://groups.google.com/group/DotNetDevelopment Google groups hoping to share my knowledge and also to get some of my own doubts clarified. I posted a technical question with my piece of code showing where I was badly stuck and I got a decent and exact answer from the moderator himself. I thanked him and posted the new code. FWIW, someone else following it up might find it useful. But then as I browsed through the questions posted and the replies made to them, I could glaringly notice the tone of the people who were replying. It clearly appeared to me as totally UNETHICAL and a lot of comments were inappropriate.

 Some of them were too demeaning that it made me think they were mere racial slurs camouflaged as outbursts of a bunch of impatient self-proclaimed “Tech Wizards”. There was one too many instances where these guys refer to its subscribers as “Silly”, “Stupid”, “Idiots”, “Incompetent” etc… This is certainly not the way to go. It makes me believe that it is run by a bunch of red necks! 

I do understand that it gets really frustrating when you see a question repeated or naïve questions asked; questions which have answers all over the internet. But then, no one is under any obligation to reply to those posts. If you think you have an answer – go ahead answer it or else JUST SHUT UP! Do not make sarcastic comments. Don’t make fun of some guy’s ignorance or idiocy for that matter. Or if you want to discuss about someone, do it personally within a private distribution list of people who would fall off their chair laughing at your stupid dirty jokes (maybe you could call it “RedNeckSuckers”? it would be a good name to start with!)

The more professional way of handling these things would be to put points like these on the rules and regulations, terms and conditions section of the group:

  1. Avoid posting questions which have already been asked. Please search the group for similar questions, before you post yours
  2. Post your questions with some example code wherever possible. Arbitrary questions will not be entertained.
  3. Users repeatedly violating these terms will be automatically unsubscribed from the list.

Unless something like this is done, it would be hard for me to believe the group is run by professionals.

Saturday, November 22, 2008

ASP.Net Impersonation Issue

A month ago a colleague came with a strange problem. They had deployed their website into production environment and was using a newly created user account for impersonation. The problem was the site would not authenticate the user and impersonation fails. However, if the user is made as an administrator, it works fine.

Apparently, the company policy does not permit such user accounts to be given admin rights.

No one from the dev team has access to the production environment and the deployment is done by onsite support guys. That is how things work here. The support people report issues and we give them different solutions to try out until the issue is fixed.

So when it came to me I did exactly the following:
I created myself a sample page to host on my local IIS
Created a test user account to play with
Pound the MSDN for some clue

I started by revoking all rights and permissions from the user account and went up stone by stone. Incidentally I stumbled upon this link: 
http://msdn.microsoft.com/en-us/library/ms998351.aspx which saved my day.

You need not make the user an Administrator, but just need to enable the “Log on locally” right.After enabling the Logon Locally right to the user, the impersonation worked like a charm.


Quoted from the link:

"You can impersonate with the returned token and check which Windows groups the user is a member of. Whether you can access local resources or network resources depends on the logon session type that you request (you specify the logon session type in the third argument of LogonUser). The most commonly used logon session types when calling this API are the following:

Interactive logon: If you need to access remote resources, request an interactive logon session. This results in a logon session that has network credentials. The user account passed to logon user must be granted the Log on locally user right.
Network logon: This establishes a logon session with no network credentials. This means you can impersonate the token and access local resources only. The user account passed to logon user must be granted the Access this computer from the network user right. By default, all accounts have this right because it is granted to the Everyone group."

Connecting using Socket vs WebRequest.GetResponse

A web team in the company where I work, had deployed a web service on two different locations to, sort of, balance the load - but in reality it was actually only making one box function as a backup for the other. They had written one common Function which retrieves the web service from either of these two locations after confirming that they are up and alive.

The function was a bit slow in its response and when it was monitored during the fine tuning phase, we were looking for alternatives to the GetResponse. I did a simple page that calls a function that loops the GetResponse to the web server and noticed that it kept failing intermittently at times for no reason and it was awfully slow.

So much for just knowing if the box is up seemed a bit too much to leave it as it is. So the failover logic was made to be based on Sockets rather than the GetResponse. What I changed it to, was to just check if the web server box was up and active in the specific given port where we have the web service running. It seemed to be a better bargain for the given situation. Here is the sample function I asked them to use:



Partial Class ServerConnectivity

Private Shared Function TryConnectingSocket(ByVal serverName As String, ByVal port As Integer) As Boolean

Dim hostEntry As IPHostEntry = Nothing
Dim result As Boolean = False
Dim ipep As IPEndPoint = Nothing
Dim dummySocket As Socket = Nothing

' Get hostEntry from server IP passed
hostEntry = Dns.GetHostEntry(serverName)

' Loop through all the IP Addresses (would be just one if you passed in the IP Address itself)
For Each ip As IPAddress In hostEntry.AddressList

ipep = New IPEndPoint(ip, port)

' Create a dummy Socket object to ping and specify the timeouts and NoDelay properties
dummySocket = New Socket(ipep.AddressFamily, SocketType.Stream, Sockets.ProtocolType.Tcp)

With dummySocket
.NoDelay = True
.SendTimeout = 1000
.ReceiveTimeout = 1000
End With

Try

' Try connecting using the endpoint which maps the port
dummySocket.Connect(ipep)

If dummySocket.Connected Then

result = True

Else

Continue For

End If

Catch ex As Exception
result = False

Finally
dummySocket = Nothing

End Try

Next

Return result
End Function

End Class

Friday, November 21, 2008

Validating File Type

A few weeks back, a colleague of mine - who had built a .Net web page to upload documents, asked me for a good technique to validate the uploaded documents.

I asked him how much he has already covered and he explained that he was using the ASP.Net Fileupload control and is validating for file extensions to be only .doc and the file size to not exceed 2MB.

Though this would work fine, there is still one loop hole. Someone could change the extension of a malicious file to .doc and upload it and bring it into the system. The users have a different module to access the uploaded documents and the uploader can then rename it to whatever file extension it originally was. We just did not want that to happen. At least the system newly built should not provide that loop hole.

The options were to check for Content-Type - which failed miserably because the Content-Type is again driven by the mere extension of the file uploaded.

Since security was the important issue and also since it did not matter whether the validation is made on the server side or client side - I suggested to check for the MIME Type and gave the following code snippet to help:


Public Function GetMimeDataOfFile(ByVal strFileName As String) As String

' Declare and Initialize variables
Dim mimeOut As IntPtr
Dim returnStr As String = ""
Dim fstream As FileStream = Nothing
Dim buffer() As Byte = Nothing
Dim urlmonResult As Integer = -1
Dim maxContentSize As Integer = 0
Dim fileInfo As FileInfo = Nothing


' Short circuit out if file not found

If Not File.Exists(strFileName) Then

Return returnStr

Else

fileInfo = New FileInfo(strFileName)


' Read the file in buffer - Initial 1024 bytes is enough to determine the mime type
maxContentSize = fileInfo.Length

If maxContentSize > 1024 Then
maxContentSize = 1024
End If


' Initialize buffer
ReDim buffer(maxContentSize)


' Open file in stream read mode - read it into buffer
fstream = fileInfo.OpenRead()
With fstream
.Read(buffer, 0, maxContentSize)
.Close()
End With

' Call our pinvoke method
urlmonResult = DllImports.FindMimeFromData(IntPtr.Zero, strFileName, buffer, maxContentSize _

, Nothing, 0, mimeOut, 0)


If urlmonResult = 0 Then

' Convert the pointer data to Unicode text
returnStr = System.Runtime.InteropServices.Marshal.PtrToStringUni(mimeOut)

End If


' Free up the pointer references
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(mimeOut)


End If

Return returnStr
End Function


You can easily get the following pinvoke signatures code from http://pinvoke.net


_

Public Shared Function FindMimeFromData( _
ByVal pBC As IntPtr, _
_
ByVal pwzUrl As String, _
ByVal _
pBuffer As Byte(), _
ByVal cbSize As Integer, _
_
ByVal pwzMimeProposed As String, _
ByVal dwMimeFlags As Integer, _
_
ByRef ppwzMimeOut As Integer, _
ByVal dwReserved As Integer) As Integer
End Function

End Class


Now even if a .jpg file is renamed as .doc and uploaded, this function would see through it and will help to notify that the content does not match the file extension.

Wednesday, November 19, 2008

Typed Functions with Parameterized Constructors

Recently I encountered this scenario where a method began to become a perfect candidate to be parameterized as I was using it for more and more newly created objects.

Private Shared Function AssembleData( _
ByVal dataList As List(Of PermissionInfoData) _
) As List(Of PermissionInfo)

Dim permissions As New List(Of PermissionInfo)

For Each data As PermissionInfoData In dataList
permissions.Add(New PermissionInfo(data))
Next

Return permissions
End Function

It is basically an Assembler method that takes a list of a "data" class and converts it into a list of "Info" Class.
As I was building more and more such Info-Data pairs of objects where required and I had to create more and more overloads for the AssembleData function.

So I quickly started writing a Typed function only to get stuck real bad in the next few seconds:

Private Shared Function AssembleData(Of TInfo As {Class, New}, TData)( _
ByVal dataList As List(Of TData) _
) As List(Of TInfo)

'Return permissions
Dim infoList As New List(Of TInfo)
Dim info As New TInfo(TData)

For Each data As TData In dataList
infoList.Add(info)
Next

Return infoList
End Function


The problem was the Info class Constructor takes the data as a parameter and would instantiate itself based on that.
I tried implementing an interface but was going in all directions without focus.

I then took help from one of the google groups and here is the solution.
.Net does not support parameterized constructors but there is this workaround which worked like charm:

Private Shared Function AssembleData(Of TInfo, TData, TAssemblerFactory As {Class, IAssemblerFactory(Of TInfo, TData), New})( _
ByVal dataList As List(Of TData) _
) As List(Of TInfo)

Dim infoList As New List(Of TInfo)
Dim info As TInfo
Dim factory As TAssemblerFactory

For Each data As TData In dataList
factory = New TAssemblerFactory
info = factory.CreateNew(data)
infoList.Add(info)
Next

Return infoList
End Function

Interface IAssemblerFactory(Of TInfo, TData)
Function CreateNew(ByVal data As TData) As TInfo
End Interface


Public Class UserInfoAssemblerFactory
Implements IAssemblerFactory(Of UserInfo, UserInfoData)


Public Function CreateNew( _
ByVal data As UserInfoData _
) As UserInfo Implements IAssemblerFactory(Of UserInfo, UserInfoData).CreateNew

Return New UserInfo(data)
End Function

End Class

The annoying security warning

A web development team in my company that was deploying a newly developed website faced the annoying security warning issue. When any page was accessed over a secure layer the browser would cause a security warning –

“This page contains both secure and nonsecure items”

When they asked me to help out, I sat down with the developer and found out where they were so far in analyzing the issue. He had his own suspicion on a menu (that they recently used downloading it free from some website) and was hitting hard on reading through the script.

Of course there was a complete possibility of the script causing the issue, we just cannot rule out the rest of the website as totally safe. So we started with a simple .htm page which caused the security warning to pop up.

First step – we saw the html source if it contained any direct http:// links. There were a couple which we promptly removed.

Secondly – we looked for any JavaScript “document.write” writing any http:// link. We also checked the CSS styles for any image URL reference or any such. – Things looked clean

Third step – enter fiddler. Trace the requests and the response. There was this one setting in the Advanced tab we had to enable for tracing the https requests and responses.
Now we found that the warning arose at the point of loading the suspicious menu.js but that was not the problem, there was a problem a few entries before – a 404 error for some stylesheet file. Apparently there was a link made to a style sheet which was moved to a different location.

Next step – fixed that error and redeployed and ran the trace again. This time the trace stopped at a different location. But yet again there was another 404 error. This one was strange since it just says the file was https://www.website.com/home/white
The actual problem was the page had defined the background attribute of the body element as white. Like this:

<body background="'white'">

What should have been bgcolor or could have been done with styles. White was interpreted as a file and the browser was expecting one.

We fixed that one too and it is now resent for yet another re-deploy. One lesson learnt is to always have a SSL enabled QA environment to test for such SSL specific issues – which could have saved loads of time.

Sybase patIndex string/column validation

Recently one of my colleagues asked me if there is any way we can validate a given string or a column in a Sybase table for containing only alphanumeric characters. Asking what exactly she wanted, I came to know that she wanted a string to just contain a-z,A-Z,0-9 and no more.

There was no IsAlpha function in Sybase AFAIK. So the suggestion was to go the other way – use “patIndex” to cancel out all the special characters.

Declare @variable1 varchar(20)
Set @variable1 = "S12345[67D"
Select patindex('%[~!@#$%^&*()_+{}"?<>:/.,'';[]\`=-]%',@variable1)


Result would be 7 (the string assigned to @variable1 has a ‘[’ at position 7)

patIndex would return a value greater than 0 if any of the special characters specified is present in the passed variable or a column value. There could be a better/simpler way of doing this, but this one quickly served the current purpose of hers.

However, a better way of doing it would be:
Declare @variable1 varchar(20)
Set @variable1 = "S12345[67D"
Select patindex('%[^a-z,A-Z,0-9]%',@variable1)

Result would be greater than zero if any character other than the one specified in the range is in the variable, in our case again 7.