giovedì 20 dicembre 2007

ResolveUrl not resolving

Since the .net conversion of SkakkiNostri, we were facing a rare and strange problem.
When the user clicks the quick Search button, it is internally redirected to a more specialized page using the line:

Response.Redirect(ResolveUrl("~/search_users.aspx?quickValue=" + txtUser.Text), true);

This line was causing 404 errors, with the ResolveUrl call apparently not doing what it has to do: the user was redirected to http://www.skakkinostri.it/~/search_users.aspx?quickValue=something.

Can you spot the problem?

Hint: today, while skimming through the error list I noticed something strange. All of these error reports were for users that contained unicode characters. Lots of them, like in the word "•]•·´º´·» .:. rO£@Lb@ ιи ℓσνє.:. [♡] «·´º´·•[•".

Then came the enlightenment: maybe the ResolveUrl call fails if the URL is not perfectly url-formatted!
And it turns out it's just that way. As simple as it seems, the offending call was promptly replaced with:

Response.Redirect(ResolveUrl("~/search_users.aspx?quickValue=" + Server.UrlEncode(txtUser.Text)), true);

and suddenly exceptions stopped.
Now let's go for a check of all the Response.Redirect calls in the web site :)

venerdì 30 novembre 2007

Custom validators and Nokia Browser

On SkakkiNostri, we have a custom RequiredFieldValidator for a custom control that basically boils down to a textarea. We had a bug report stating that people using the Nokia S60 built-in browser always failed the required field validation. Well, it turns out that the standard .net ValidatorTrim function simply does not work there, probably has something to do with the order of loading of scripts, or maybe our code isn't perfect (I did not try to write a test case, and standard RequiredFieldValidators work just fine). Since I do not have much time at all, I just wrote a simple TrimString function and now we can post messages in SkakkiNostri's forum using the Nokia Browser :)

Code:

// On Nokia Browser, the .net ValidatorTrim always returns null.
function TrimString(s) { return s.replace(/^\s+/g, "").replace(/\s+$/g, ""); }

lunedì 2 luglio 2007

The downlevel W3C html validator

ASP.net is smart enough to know which html content render to which browser, which is good. However there's a small flaw in the browser detection: the W3C html validator is detected as pre-DOM (and since it's not a real browser it logically is one of those), but that defeats the purpose of validating the output of a website.
Luckily it's quite easy to fix this issue. Just create a folder called App_Browsers in your website root and place a file called w3cvalidator.browser, containing the following code:

<browsers>
<!--
Browser capability file for the W3C validator
Sample User-Agent: "W3C_Validator/1.432.2.22"
-->
<browser id="w3cValidator" parentid="Default">
<identification>
<useragent match="^W3C_Validator">
</identification>
<capture>
<useragent match="^W3C_Validator/(?'version'(?'major'\d+)(?'minor'\.\d+)\w*).*">
</capture>
<capabilities>
<capability value="w3cValidator" name="browser">
<capability value="${major}" name="majorversion">
<capability value="${minor}" name="minorversion">
<capability value="${version}" name="version">
<capability value="1.0" name="w3cdomversion">
<capability value="true" name="xml">
<capability value="System.Web.UI.HtmlTextWriter" name="tagWriter">
</capabilities>
</browser>
</browsers>

One small side note. Initially I was placing this file along with another .browser file that I use to render PNG images with transparences (will talk about this little gem in the future). That one was called All.browser and rendered the right code for all browsers, but somehow it prevented the use of the w3cvalidator.browser file. Merging those two files into one did the trick.

mercoledì 13 giugno 2007

Preventing double postback

Today I had to make sure a certain button could not be pressed twice, to avoid processing errors. Initially I just set the disabled property to true, but that would not work since postback from disabled controls is inhibited. I invoked manually the postback, and it worked, but broke when validators prevented the postback. After a little bit of wondering, I crafted this small routine, to be placed in a Page-derived class for simplicity.

/// <summary>
/// Makes a certain button one-shot only,
/// preserving validator functionality
/// </summary>
/// <param name="btn">The button that can't be clicked twice</param>
protected void RegisterSinglePostButton(Button btn)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("if (typeof(Page_ClientValidate) == 'function') { ");
sb.Append("if (Page_ClientValidate() == false) { return false; }} ");
sb.Append("this.disabled = true;");
sb.Append(ClientScript.GetPostBackEventReference(btn, ""));
sb.Append(";");
btn.Attributes.Add("onclick", sb.ToString());
}

It can be easily transformed in a control if needed, for further clarity.

sabato 9 giugno 2007

A small step for a man

Just a small accomplishment, yet I'm very happy. In the last days I was having a lot of 404 reports for a no-longer existing page on SkakkiNostri. The referrer page was null, so I had to find the source myself. I grep'd the source code, queried nearly every table in the database, but there was no link to that page there. So chances were a) some search engine b) some user.
Looking at the UserAgent/IP address, I quickly found it wasn't a search engine. So probably it had a user that dumbily had bookmarked that page, and now used my 404 error page to come back to the site. I have been thinking about what I could do to avoid this error, then I had a sudden realization: I could just track down the user using the cookies collection and send him a private message. Guess what? It worked. The user wasn't understanding what a 404 error was, but could access the site, so he was happy. Nonetheless explaining the situation to him we have been to fix the wrong link in the Favorites, and I'm no longer receiving emails. Well, not for that error :)

mercoledì 30 maggio 2007

Score another one for me!

So, after a clean format of my machine, I had this weird error in Microsoft Visual Studio 2005: it wouldn't compile one of my web projects. Compilation failed with the following error:

(O): Build (web): Unable to find the required module. (Exception from HRESULT: 0x8007007E)

As you can see, it says nothing about WHICH module is missing. I tried for a few days to fiddle with dependant assemblies, web.config settings, etc. but only today I have been able to investigate and fix it. I downloaded ProcessMonitor from the Microsoft site and after defining the appropriate filters I tracked down the missing assembly: it was MSVCR71.DLL. Just found a copy on some other location on the HD, placed it into C:\Windows\System32 and it worked flawlessly.

I feel like I deserve a reward. Going to buy an icecream! :-)

domenica 13 maggio 2007

WTF?

While checking the error logs for SkakkiNostri, this morning, I noticed something strange. Yesterday night something like a spider crawled my site, querying the robots.txt as usual, but then it started messing with the file paths.

It tried to visit the pages listed in the web site root (e.g. /forum.aspx), but under the /admin/ folder (/admin/forum.aspx). These do not exist, so I received a notification of the 404 error.

The user agent for those request is Java/1.5.0_11, and the originating host is a dynamic IP of the Telecom Italia range. So maybe one of my user tried a lame scanning tool, but that IP was not in the http logs. How strange...

However I am starting to be annoyed by these notifications. Please, fix that scanning tool! :-)

sabato 12 maggio 2007

Now I have no excuses

...for not documenting my code, that is. I just installed the excellent and free GhostDoc plugin for Visual Studio 2005. Simple and neat, with a right clic on a function prototype this is what is does:

/// <summary>
/// Processes the HTML message.
/// </summary>
/// <param name="message">The message.</param>
/// <returns></returns>
public static string ProcessHtmlMessage(string message)
{
[ ... ]
}

venerdì 11 maggio 2007

A little gem

I always liked the way Opera remembers the tabs that were open when it gets closed. Today I looked for a similar Firefox extension, and after a little search I found it is already present in the browser! Just enable it in Tools > Options > Main > Startup. Cool!

A rant

While surfing the net, I usually visit just a handful of sites, and usually three-four times a day.

Now, three of them have video ads. I just can't stand them. Just move the mouse above them, say to click on a link, and they start playing. One of them even starts without intervention. ARGH! It's a long time since I subconsciously installed a brain filter for moving images in web pages, but now they have audio! Imagine my beloved classical music overtaken by a loud voice saying "Take control of the compliance!".

Stop now, please. PLEASE! Video adverts make baby Jesus cry!

mercoledì 9 maggio 2007

SmtpExceptions

While migrating SkakkiNostri from classic ASP to ASP.NET, I noticed quite a lot of unhandled SmtpExceptions. I was fairly shocked, since, before the migration, I did not have any feedback about the relaying of the E-Mails sent by the site, and thought errors were a lot more infrequent.
Right now, my inbox is full of SmtpException notifications. I need to do something to track them in an appropriate place, possibly filtering and grouping notifications. Any ideas are welcome.

Reminder to myself

Request.UserAgent can be null (when the client does not specify it). I was trying to detect the Googlebot and be nice with it, and later found it failed with a few other requests because of a missing null check. DOH!

How to escape apostrophes in XPath / .net

Yesterday I found something interesting: there isn't a correct way to escape string literals in XPath queries.

Example:

galleryDocument.SelectSingleNode("//photo[filename='" + photoFileName + "']");

This line raises an exception if photoFileName contains an apostrophe. Neither ' nor \' nor '' work as an escape sequence - the XPath specifications do not handle that. So, how do we deal with it?

The answer is a small routine that relies on the concat XPath function:

public static string EscapeApostropheForXPathParameter(string parameter)
{
if (!parameter.Contains("'")) return "'" + parameter + "'";

string[] parts = parameter.Split('\'');

string result = String.Empty;
foreach (string part in parts)
result += ", \"'\", '" + part + "'";

return "concat(" + result.Substring(7) + ")";
}

This routine basically maps the string "foo'bar" into concat('foo', "'", 'bar'). Please note the different kind of quotes around the apostrophe. Nice to know, huh? :-)