Saturday, September 30, 2006

app offline page

Thought of using app_offline feature and display the system down page for an webapp update happening today, if you haven't heard about this, here is a fly-by, if asp.net 2.0 engine sees this file in app root dir, it shuts app domain and serves this file to clients, brings back the app domain after this file is deleted.

Unfortunately clients needed to test the app during the down time and hence i need to allow list of users and server down message for rest, i started coding this logic in Application_AuthenticateRequest , ahhh what am i thinking coding this in app and i need to move the app again to remove this logic.

Finally wrote this in a HttpModule and wired it in the request pipeline, once testing is done its as simple as deleting off that 1 line in web.config. For more details on
HttpModule, here is one i wrote for some other reason.

Thursday, September 28, 2006

Ajax Ondemand Tooltip


Client wanted to show a tooltip containing details for key column on mouseover. After talking to him 2 mins, it is clear that they really don't need this for every key showed in the screen, rather interested only a select few. Typical case for on-demand tooltip, i should be lucky today, i happen to cross the overlib tooltip js and ASP.net Ajax WS call to get the details and display a overlib tooltip on dblclick of text and it can be either closed manually or it'll autoclose in 5 secs.


Probably something likethis or intelliTxt might show up in the control toolkit. Here is the sample code.

Tuesday, September 26, 2006

web.config configuration node xmlnamespace issue

I had developed and using a data access component which readds the db configuration from app.config. Today i was told it is not working and it indeed was not able to select the db config details node. After few mins of hit-n-miss, the issue is due to xmlnamespace attrib in configuration node. Once i remove the xmlns , i was able to select the specified node

<
configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"
>
<dataConfig ...

XmlNode enterpriseLibraryDataNode = appConfigDoc.SelectSingleNode(@"/configuration/
dataConfig");

Not sure from where the dev team got this namespace from, googling a bit tells me its a VS 2005 beta release bug. Whatever the case, i hate to find a simple attribute crashing straight forward xml dom select node code.

Saturday, September 16, 2006

downloading streaming media ...

I get this request many times, how to download streaming media, like download song from raaga.com or download movie from tamilgrounds.com. I'm not going to give lot of details , to put it simply *anything* playable from internet should be downloadable, all we need to find underlying media url and save the file.

Many a times finding the playing media url is going to be bit tricky, we should be able to get it using any packet sniffer, I use very much tailored app for this URL Snooper and use Flashget as download manager, but any packet sniffer should do.

Signing off with link to tamil drama's (credits Gg)

Friday, September 15, 2006

Melissa Theuriau

That's a gd reason to learn french ;-)

--flickr slides--

Tuesday, September 12, 2006

Ready Set Grow case study from Microsoft

I was searching for some MSFT article for the F5 IIS session bump issue and sporadically searched for schwan and landed in the RSG case study. Not sure this is only version MSFT has.

Right bar lists, Microsoft BizTalk Server 2006 & Microsoft Visual Studio 2005 in products and technologies. Hmm how come VS2005 & Biz2006 was used, when we started project in Jan '04

:-)

Thursday, September 07, 2006

ASP.net session bump/hijack issue - F5 & IIS cluster

I haven't seen this issue personally, but it is confirmed from multiple sources this happens. In short: user rarely bumps into another user's session, in verbose: user logs into web app and uses it for a while and all of a sudden gets data that is not in his session.

Current prod infrastructure is IIS cluster behind F5. Session affinity is set to 20 mins and IIS session time-out is set 20 mins. Session location is in-proc.

I have seen request getting postedback to different webserver and hence viewstate validation fail with exception CryptographicException: Bad Data. Authentication of viewstate failed. ...
i don't know how F5 works but i guess F5 lost identifier of the user and hence thinks it as new user and routes request to different web server. This is a normal issue and we can either turnoff viewstate encryption or explicitly specify viewstate encryption key in all servers in cluster.

But i have to totally rule out F5 from the session bump/hijack issue, 'coz F5 has no idea what asp.net session is, that is something internal to aspnet_wp.exe (IIS5) or w3wp.exe (IIS6).

Under the hood, aspnet maintains the session id using ASP.NET_SessionId cookie and IIS can guarantee its uniqueness across its requests. Here is an MSFT KB about reusing session id for apps in same DNS domain and this article explains behind the scene how asp.net serves request.

I'm guessing this could be the cause,
userA hits webserver1 in cluster in the initial request and F5 should take us to the same server for subsequent requests.
If F5 sends the userA's request to webserver2 in middle of the session, and by happenstance (theoretically this may be possible) userA's ASP.NET_SessionId is already assigned by webserver2 to some other user (say userB), then aspnet engine will think userA as userB and assign userB's sessionstate to this request.

Pls leave a note if you think this may be possible or how you think this could be happening.

I expect IIS team to provide option to make the sessionid unique in a cluster environment. For now what we planned is to generate a unique sessionid and store it in cookie as well as session, check if they match for every request, if not kill the session, this is going to kill both user's session, but i don't know if there is any way out.

I didn't want to include tis logic in existing app 'coz 1 i'll have to include this logic in every app i write and 2 i'm too lazy. I wrote following HTTPModule and hooked it to web app, ideally something like this should be configured in machine.config so that we don't need to do this every web.config, netiher i don't have authority nor i want to pursue this.

Right now code is written to send a mail when something like this happens (just dropping a line to see if something takes this bait) and it will redirect to itself, so page is written poperly to handle empty session which i expect to be a norm.

using System;
using System.Web;
using System.Web.SessionState;

namespace KCC
{
public class SessionCheck : IHttpModule
{
public SessionCheck() {}
public void Dispose() {}
public void Init(HttpApplication httpApp)
{
httpApp.PreRequestHandlerExecute += new EventHandler(this.PreRequestHandlerExecute);
if (httpApp.Modules["Session"] != null)
{
SessionStateModule session = (SessionStateModule) httpApp.Modules["Session"];
session.Start += new EventHandler(this.OnSessionStart);
}
}

public void OnSessionStart(object sender, EventArgs e)
{
HttpCookie sessionChk = new HttpCookie("KCC.SessionCheck");
sessionChk["iSAS.SessionID"] = Guid.NewGuid().ToString();
sessionChk["iSAS.MacName"] = System.Environment.MachineName;
sessionChk["iSAS.SesStrtTmstmp"] = System.DateTime.Now.ToString();
HttpContext.Current.Response.AppendCookie(sessionChk);
HttpContext.Current.Session["iSAS.SessionID"] = sessionChk["iSAS.SessionID"];
}


private void PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication httpApp = (HttpApplication) sender;
HttpSessionState session = httpApp.Context.Session;
if (session != null) //skip pages which don't use session
{
if(session["iSAS.SessionID"] != null) //skip if session value is empty
{
if (!httpApp.Context.Session.IsNewSession) //skip if new session
{
string szCookieHeader = System.Web.HttpContext.Current.Request.Headers["Cookie"];
if ((null != szCookieHeader) && (szCookieHeader.IndexOf("ASP.NET_SessionId") >= 0)) //skip if new session 2
{
if (httpApp.Request.Cookies["KCC.SessionCheck"] != null) //skip ifCookie not found
{
if (httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SessionID"] != null) //skip if cookie data not found
{
if (session["iSAS.SessionID"].ToString().Trim() != httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SessionID"].ToString().Trim())
{
System.Text.StringBuilder info = new System.Text.StringBuilder();
if (httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SesStrtTmstmp"] != null)
info.Append("Session start time: " + httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SesStrtTmstmp"].ToString());
info.Append("<br/>");
if (httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.MacName"] != null)
info.Append("Session start web server name: " + httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.MacName"].ToString());
info.Append("<br/>");
info.Append("Current time " + System.DateTime.Now.ToString());
info.Append("<br/>");
info.Append("Current web server " + System.Environment.MachineName);
info.Append("<br/>");
info.Append("Logged on NTID" + httpApp.Context.Request.ServerVariables.Get("LOGON_USER").ToString());
info.Append("<br/>");
if (session["NTID"] != null) info.Append("Current NTID in session" + session["NTID"].ToString());
info.Append("<br/>");
info.Append("URL " + httpApp.Request.Url.ToString());
info.Append("<br/>");
System.Web.Mail.MailMessage msg = new System.Web.Mail.MailMessage();
msg.To = "me@mail.com";
msg.Subject = "-- SESSION HIJACK ISSUE --";
msg.From = "me@mail.com";
msg.Body = info.ToString();
msg.BodyFormat = System.Web.Mail.MailFormat.Html;
try
{
System.Web.Mail.SmtpMail.SmtpServer = "mailhost.com";
System.Web.Mail.SmtpMail.Send(msg);
}
catch //do nothing
{}
httpApp.Context.Response.Cookies["KCC.SessionCheck"].Expires = DateTime.Now.AddYears(-1);
session.Abandon();
httpApp.Context.Response.Write(httpApp.Context.Request.Url.AbsoluteUri.ToString());
httpApp.Context.Response.Redirect(httpApp.Context.Request.Url.AbsoluteUri.ToString(),true);
httpApp.CompleteRequest();
}
}
}
}
}
}
}
}
}
}

For further reading, Foiling Session Hijacking Attempts and HTTP application executes events sequence.

Chennai satellite town

Irrespective of what Ramadoss/JJ/KK thinks about the chennai satellite town, this is going to come up. Reason cited for objection "destroying fertile lands", CRAP! what if NY residents opposed to so called encroachment of urban buildings some 100 yrs back; ok tamil heroine's will have to look for different city to dance around, nothing much.

Current facilities is super sufficent for chennai city (if we can roll back 10 yrs). What leaders don't have is a vision (Dr. APJ strsses this in almost all his speeches), even the most hated Hitler had a vision to make Germany a modern nation, if not they will not have Autobahn now. Hmm, why should J needs to worry if she has to identify something everyday to spit out "arrikai".

Nothing is going to improve if TN politics is going to roll around J & K.

Vande Mataram!
:-s

Wednesday, September 06, 2006

asp:CustomValidator changing ErrorMessage displayed

requirement: custom validate the amount value; is required only if ddlContributionType value is 2 or 3.

code:
<!-- aspx -- >
<asp:dropdownlist id="ddlContributionType" runat="server">
<asp:textbox id="txtAmount" runat="server">
<asp:customvalidator id="CustomValidateAmount" runat="server" controltovalidate="txtAmount" errormessage="Invalid" clientvalidationfunction="CustomValidateAmount_ClientValidate" onservervalidate="CustomValidateAmount_ServerValidate" />

function CustomValidateAmount_ClientValidate(oSource,oArguments)
{
var ddlContributionType;
if (oSource.getAttribute("ContributionType") != null)
{
ddlContributionType = document.getElementById(oSource.getAttribute("ContributionType"));
}
if (ddlContributionType != null)
{
if ( (ddlContributionType.value != "2") && (ddlContributionType.value != "3") )
{
oArguments.IsValid = true;
return;
}
}
else
{
oArguments.IsValid = true;
return;
}
var amount = oArguments.Value;
oArguments.IsValid = false;
if ( isNaN(amount) == true)
{
oSource.innerHTML = "Invalid amount";
return;
}
var re = new RegExp("^\\d+(\\.\\d{1,2})?$");
m = amount.match(re);
if (m == null)
{
oSource.innerHTML = "Invalid amount format";
return;
}
if ( amount < innerhtml = "Minimum donation of $1 required"> 9999.99 )
{
oSource.innerHTML = "Maximum donation is $9999.99";
return;
}
oArguments.IsValid = true;
}

' .aspx.vb Page_Load
CustomValidateAmount.Attributes("ContributionType") = ddlContributionType.ClientID.ToString()

I explicity not included the check if required for condition 2 or 3, as i re-use an existing validator (RequiredFieldValidatorWithAEnablingCondition) that i developed long back, will mail the code if requested

<KCC:RequiredFieldValidatorWithAEnablingCondition runat="server" ID="RequiredFieldValidatorWithAEnablingConditionAmount" ControlToValidate="txtAmount"
ControlToCompare="ddlContributionType" TriggerValue="2;3" ErrorMessage="Required." />

Monday, September 04, 2006

Is life hitch hiking ?

Today saw Discovery documentry on panspermia, wonder what finally turned out in "kerala red rain". School taught the evolution theory, temple preached "intelligent design", after all who cares about these, idea is to stuff the answer paper and get results in mark sheet.
I think i really didn't take any side here, where do U stand ?

Saturday, September 02, 2006

LDAP authentication in website

Got a requet from a client to provide LDAP authentication in exisiting ASP website, in plain terms authenticate users using thier domain id and password. Initial thought of intergrated win auth was ruled out 'coz of Kiosk based computers used. Should make sure we SSL the page.

'ASP code
Const ADS_SECURE_AUTHENTICATION = &H1
On Error Resume Next
Err.Clear
Set dso = GetObject("LDAP:")
Set domain = dso.OpenDSObject("LDAP://server.com", strUserName, strUserPassword, ADS_SECURE_AUTHENTICATION)
If Err.number <> 0 then
Response.Write("Invalid login. Err.number=" & Err.number)
Else
Response.Write("Valid login. Err.number=" & Err.number)
End If
Set dso = Nothing
Set domain = Nothing

Also same functionality in ASP.net 1.0 http://support.microsoft.com/?id=326340.

dinamalar epaper

Its becoming almost an habit to read dinamalar in original paper format. Today i thought of scrapping the content for offline viewing, it was rediculously simple, here goes the code.


using System;
using System.Drawing;
using System.Text;
using System.Net;
using System.IO;

namespace GetDinamalar
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
DoWork();
}
static void DoWork()
{
// /Web/PagePrint/YYYY/MM/DD/DD_MM_YYYY_001.pdf
try
{
int pgCtr=1;
Console.WriteLine("Booting up ....");
string folderpath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()),DateTime.Now.ToString("MM.dd.yyyy") );
Directory.CreateDirectory(folderpath) ;
WebProxy proxy = null;
Microsoft.Win32.RegistryKey reg1 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
if (reg1.GetValue("ProxyEnable", "1").ToString() == "1")
{
proxy = new WebProxy("http://" + reg1.GetValue("ProxyServer", "proxy.isas.com:80").ToString(),true,null,CredentialCache.DefaultCredentials);
}
reg1.Close();
while(true)
{
Console.WriteLine("Sending request to web for page " + pgCtr.ToString());
string address = "http://epaper.dinamalar.com/Web/PagePrint/" + DateTime.Now.ToString("yyyy/MM/dd/dd_MM_yyyy_") + pgCtr.ToString().PadLeft(3,'0') + ".pdf";
WebRequest httpRequest = WebRequest.Create(address);
if(proxy != null) httpRequest.Proxy = proxy;

Stream ReceiveStream = null;
try
{
ReceiveStream = httpRequest.GetResponse().GetResponseStream();
}
catch
{
return;
}
string filePath = Path.Combine(folderpath ,DateTime.Now.ToString("yyyy_MM_dd_") + pgCtr.ToString().PadLeft(3,'0') + ".pdf");
FileStream fs = new FileStream(filePath, FileMode.Create,FileAccess.Write);
BinaryWriter binWrt = new BinaryWriter(fs);
binWrt.BaseStream.Seek(0, SeekOrigin.End);
int data=0;
byte [] buffer = new byte [1024];
while ((data=ReceiveStream.Read(buffer,0,1024))>0)
{
binWrt.Write(buffer,0,data);
}
binWrt.Flush() ;
binWrt.Close() ;
pgCtr++;
}
}
catch (Exception exception)
{
Console.WriteLine("Error:" + exception.ToString());
Console.Read();
}
finally
{
Console.WriteLine("Exiting process ...");
}
}
}
}

Back from Hibernation :-)

It is almost 3 months i blogged, got myself tied up for happier personal reasons. To re-start with c# (.net 1.x) code to scrap daily dilbert comic and set it as desktop wallpaper.

Due to frequent demand for this, download the application here. Copy GetDilbert.exe to a folder and create a shortcut in windows startup.

using System;
using System.Drawing;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;

namespace GetDilbert
{
class Class1
{
private const int SPI_SETDESKWALLPAPER = 0X14;
private const int SPIF_UPDATEINIFILE = 0X1;
private const int SPIF_SENDWININICHANGE = 0X2;

[DllImport("USER32.DLL", EntryPoint = "SystemParametersInfo", SetLastError = true)]
private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);

[STAThread]
static void Main(string[] args)
{
DoWork();
}

static void DoWork()
{
try
{
Console.WriteLine("Booting up ....");
string address = "http://www.dilbert.com/comics/dilbert/archive/";
WebProxy proxy = null;

WebRequest httpRequest = WebRequest.Create(address);
Microsoft.Win32.RegistryKey reg1 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
if (reg1.GetValue("ProxyEnable", "1").ToString() == "1")
{
proxy = new WebProxy("http://" + reg1.GetValue("ProxyServer", "proxy.isas.com:80").ToString(),true,null,CredentialCache.DefaultCredentials);
}
reg1.Close();
string streamText = null;
Console.WriteLine("Sending request to web ...");
if(proxy != null) httpRequest.Proxy = proxy;
using (StreamReader rdr = new StreamReader(httpRequest.GetResponse().GetResponseStream(), System.Text.Encoding.ASCII))
{
streamText = rdr.ReadToEnd();
}
Console.WriteLine("Scrapping web response ...");
Match match = Regex.Match(streamText,@"/comics/dilbert/archive/images/(\w+).(gifjpg)",RegexOptions.IgnoreCase);

if (match != Match.Empty)
{
string imageLocation = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()),"Dilbert_" + DateTime.Now.ToString("MM.dd.yyyy") + ".bmp");
Console.WriteLine("Downloading todays comic ...");
httpRequest = WebRequest.Create("http://www.dilbert.com" + match.Value);
if(proxy != null) httpRequest.Proxy = proxy;
using (Bitmap bmp = new Bitmap(httpRequest.GetResponse().GetResponseStream()))
{
bmp.Save(imageLocation, System.Drawing.Imaging.ImageFormat.Bmp);
}
Console.WriteLine("Setting as desktop wallpaper...");
Microsoft.Win32.RegistryKey reg = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
reg.SetValue("TileWallpaper", "0");
reg.SetValue("WallpaperStyle", "0");
reg.Close();
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, imageLocation, SPIF_UPDATEINIFILE SPIF_SENDWININICHANGE);
Console.WriteLine("Shutting down app.");
}
}
catch (Exception exception)
{
Console.WriteLine("Error:" + exception.ToString());
Console.Read();
}
}
}
}