Friday, November 28, 2008

How to remove corrupted web part from Sharepoint pages

Sharepoint doesn't store a physical aspx pages. They stored the pages in database and when an aspx have erratic web parts you cannot access the page from sharepoint designer in anyway to take away the web part that is causing the problem. And this issue could lead the sharepoint page not being able to render. One way to remove the problematic web part while having trouble accessing the page which host the web part is to get access to web part maintainance page. By appending ?contents=1 to the sharepoint page, you will be able to access to web part maintainance page and will able to remove the erratic web part. The link you should enter to access the web part maintainance should look something like http://mysharepointserver.com/default.aspx?contents=1

Once get access to the web part maintainance page you can either delete, close or reset the web part that is causing the problem.

Tuesday, November 11, 2008

Autheticating HTTP Basic Authentication with X509 Certificate on SSL Web Service On WCF

This takes me quite awhile to figure out how to get it done on WCF. My requirement is I need to construct a client to connect to a web service with X509Certificate and HTTP Basic Authentication is needed to authenticate myself to web service. This is dual authentication mechanism. Although I can find out how to use X509 Certificate relatively easy it took me some time to play around with my config file to get the HTTP Basic Authentication correct. Here's what I did

First, I need to get the x509 behavior declare under

<behaviors>
<endpointbehaviors>
<behavior name="X509DigiCert">
<clientcredentials>
<clientcertificate findvalue="Myx509Cert" storelocation="CurrentUser" x509findtype="FindBySubjectName">
<servicecertificate>
<authentication certificatevalidationmode="None">
</servicecertificate>
</clientcredentials>
</behavior>
</endpointbehaviors>
</behaviors>

Next I need to create my own custom binding. You need to specify authenticationScheme="Basic" for Basic Http Authentication and requireClientCertificate="true" to tell WCF we are using X509 Certificate.


<!-- Use custom binding for HTTP Basic Authentication + X509 Certificate Authentication.-->
<custombinding>
<binding name="OtherPublishedDataWebServiceServiceSoapBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00"
sendTimeout="00:01:00" >
<textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
messageVersion="Soap11" writeEncoding="utf-8">
<readerQuotas maxDepth="100000" maxStringContentLength="100000"
maxArrayLength="67108864" maxBytesPerRead="65536" maxNameTableCharCount="100000" />
</textmessageencoding>
<httpsTransport manualAddressing="false" maxBufferPoolSize="67108864"
maxReceivedMessageSize="67108864" allowCookies="false" authenticationScheme="Basic"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
keepAliveEnabled="true" maxBufferSize="67108864" proxyAuthenticationScheme="Anonymous"
realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
useDefaultWebProxy="true" requireClientCertificate="true" />
</binding>
</custombinding>

Lastly, identify endpoint and make sure you include behaviorConfiguration with the previously declared behavior name X509Digicert and for binding configuration use the custombinding that we have create above. It should look something similar to this.

<endpoint address="https://mywebservices.com/OtherPublishedReports"
behaviorConfiguration="X509DigiCert" binding="customBinding"
bindingConfiguration="OtherPublishedDataWebServiceServiceSoapBinding"
contract="OtherPublishedReports.OtherPublishedDataWebService"
name="OtherPublishedDataWebServiceServiceSoapBindingQSPort" />

For Basic HTTP Authentication User Name and Password you have to include in your code. Sample code snippet below.

Dim report As OtherPublishedReports.OtherPublishedDataWebServiceClient = New OtherPublishedReports.OtherPublishedDataWebServiceClient()
'Adding Credential
report.ClientCredentials.UserName.UserName = "User"
report.ClientCredentials.UserName.Password = "Password"

Tuesday, October 21, 2008

WCF overview

WCF(Web Communication Foundation) technology is using multiple layer in configuring web service. The three main layers are

1. Address - to specify where is your web service is

2. Binding - to specify how we are going to connect to the web service. For example, using http or https or tcp. Whether we want to use X509Certificate or we want to use Windows Authentication or NTLM or Keboros. And what's is the file upload/download behavior etc

3. Contracts - This is where you write functions for web services. This is the business logic area where you write all your coding based on business logic.

People refers these three layers as A(Address), B(Binding) and C(Contracts) of WCF.

The Address and Binding can be specify either in the coding or in web.config file. The best practise is to specify the Address and Binding in web.config file. The reason is simple. The address and binding can be keep changing based on IT infrastructure requirement and configuration. If we specify the address and binding, when we change the web service hosting environment we have to change the configuration inside the coding and re-compile and re-deploy which is actually quite tedious. If you configure everything in web.config, the moment you need to make changes to the web service address, or certificate or authentication all you need to do is just open up web.config file and make the changes. There is no need to re-compile the code at all.

Other advantage of WCF is it's interoperability. WCF can communicate better with other Web Service technologies like Java, Message Queue, Remote Hosting, COM + etc.

Sunday, October 19, 2008

File streaming limitation on WCF

When I started testing out WCF for web services using VS 2008 the first issue I encounter is when I try to stream the file from my client pc to web services. By default WCF allow ridiculously small file size to stream through internet. The way to work around from this problem is to edit the web.config file in your WCF project. Below is the example

You need to define the binding configuration first

<bindings>
<basichttpbinding>
<binding name="uploadBinding" maxreceivedmessagesize="9223372036854775807" transfermode="Streamed" sendtimeout="00:10:00">
</binding>
</basichttpbinding>
</bindings>

And at endpoint configuration you need to reuse the binding name (in this example uploadBinding) so that endpoint will know what is the size WCF is allowing to receive. I put the maximum digit for maxReceivedMessageSize in the example. You can actually define a lesser limit if your web service is not intended to process a very large file

<endpoint address="" binding="basicHttpBinding" bindingconfiguration="uploadBinding" contract="Demo.WCFService.IService1">

The next issue in streaming file is when you upload a XML file you can run into the error message similar to this

The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 2302.'. Please see InnerException for more details.

It is because the MaxArrayLength allow in web.config by default is just 16384 bytes which is 16 kb. I wonder how many xml file we use for storing of data could be below 16 kb. Really!.. This is so amazing!!

You can resolve this issue by amending web.config file using Tool->WCF Configuration Tool from VS 2008 Editor, loading the existing web.config file and change the parameter values accordingly in ReaderQuotas section.

This should resolve most of the issues with uploading streaming content or downloading them.

Just take note of the two other facts below

1. Only the following binding schemes including BasicHttpBing, WebHttpBinding, NetTcpBinding and NetNamedPipeBinding are allowed to transfer stream data.
2. Transferred data must be serializable Stream data or MemoryStream data.

If the data is not serializable you will encounter error.

Tuesday, September 30, 2008

Weird problem with Sharepoint Permission

This week I encountered a weird problem. I tried to give user permission to 1 of the web site my company's user created. After I give the user permission as usual I still cannot access the page. Then I try to give full permission for the account. Still cannot. After some investigation, I found out this problem.

Site Administrator never check in the Master Page template and the template is in draft state. Since the template is in draft state it is only accessible by administrators and the template owner for this case. Other users cannot see the page because the Master Page template is not accessible. I asked the user to check in and approve the template. After that the problem is resolved! What a tricky issue!

Saturday, September 20, 2008

A little flavour of LINQ

LINQ, Language INtegrated Query is a new programming feature that's integrated with the new release of Visual Studio 2008. I've been really fancy about the creation of LINQ. Actually it is a language that tried to gap between different datasources such as XML, Object, Database and Dataset using a Simple to understand SQL query like language. I'm playing around with querying XML data using LINQ at my work for my web service project since I'm dealing with alot of transformation between XML, Object and Database in my project. I need to download XML string from my web service. Then need to insert XML data into Database and later retrieve data from database into our programming language to use as data for my objects. So LINQ naturally become one of my choice. I just want to share a little on how easy it was to parse an XML using LINQ. For example I got the XML file with the data below

<contacts>
<contact contactId="2">
<firstName>Leo</firstName>
<lastName>Lu</lastName>
<phone>91234567</phone>
<age>26</age>
</contact>
<contact contactId="3">
<firstName>James</firstName>
<lastName>Ng</lastName>
<phone>61234567</phone>
<age>32</age>
</contact>
......
</contacts>

Imagine the above XML list with lots of contacts. And I want to find my particular using LINQ, this is how I have to query it

var xmlSource = contacts.Load(@"../../Contacts.xml"); //load your xml file

// This query will search user from my XML file where first name is equal Leo and select the first name and last name together
var query = from c in xmlSource.contact
where c.firstname.value = "Leo"
select c.firstName + " " + c.lastName;


foreach(string name in q)
Console.WriteLine("Customer name = {0}", name);

Output will be

Customer name = Leo Lu

How cool is that? If you do the XML parsing before, it is not very intuitive previously and most probably you may need a few loops and if statement if you have a number of things to filter out. WIth LINQ, querying effort is much simplified.

Friday, September 12, 2008

Possible to run .NET on other platform?

The answer is yes! But not all codes might be working 100%. But it's a good options for some organisation which has already invested alot on non-windows platforms but wanted to adopt .NET technology. Here's the website

http://www.mono-project.com/Main_Page

Back Run Console Program which deal with parsing date parameters..

Sometimes we wrote console programs which taking in a date as a parameter to query or do some actions for a particular date. But what if you wanted to back run for a range of date and don't want to change the design of the original console program or you don't have access to the code of the console program? Write a wrapper program!! =) Of course your original console program must be able to take in the date as a parameter.

How to use?

Backrun.exe 01/09/2008 31/09/2008

This will run the console program you specify inside the code from 1st to 31st of September 2008.

Module BackRun

Sub Main(ByVal args As String())
Dim StartDate As Date
Dim EndDate As Date
Dim NumberOfDays As Integer
Dim counter As Integer
Dim i As Integer
Dim run As String = "D:\YourConsoleProgram.exe"
i = 0
If args.Length <> 2 Then
Console.WriteLine("Please enter from date and to date to define back run period")
Else
For Each backDate As String In args
If i = 0 Then
StartDate = DateAndTime.DateValue(backDate)
End If
EndDate = DateAndTime.DateValue(backDate)
i += 1
Next
If DateTime.Compare(StartDate, EndDate) > 0 Then
Console.WriteLine("Start Date should be earlier than End Date")
Else
NumberOfDays = EndDate.Subtract(StartDate).Days
Console.WriteLine("Number of Days " & NumberOfDays)
For counter = 0 To NumberOfDays
Try
Shell(run & " " & StartDate.AddDays(counter), AppWinStyle.MaximizedFocus, True)
Catch ex As Exception
Console.Write(ex.Message)
End Try
Next counter
End If

End If
End Sub

End Module

How to log Site Usage Data for Audit Purpose on Sharepoint 2007

My company users come up with this task when we started exploring the Site Audit feature on Sharepoint. Sharepoint has Built-in Site Auditing features by Microsoft which site administrator can turn it on, on the sharepoint site and monitoring user's activities such as creating post, editing, deleting etc. But it's simply too much information when it is turn on and retrieving of data can be quite tedious on the live server with alot of users activities going on. All we wanted to do is just monitoring the user access on some of the sites. So we tried out Site Usage Analysis features, which is another alternative by Microsoft to monitor the Site Usage for audit. But Site Usage mainly focused on showing which pages are being access by which user for site administrator to monitor the usage. Everything seems fine. But we hit 1 problem. We don't like the default presentation by Microsoft. So we tried to use Sharepoint API to retrieve the Site Usage. There's 1 great article here.

http://www.15seconds.com/issue/050623.htm

But this doesn't meet our requirement. The weaknest or rather Microsoft conservative way of implementing API is that, Sharepoint is only caching 1 month data inside database and you can only retrieve the 1 month usage data using Sharepoint API provided by Microsoft. But for our audit requirement we need more than 1 month. We wanted to keep about 1 yr site usage data for audit.

I've made some consultation with Microsoft Product Support team and we decided to parse the site usage data from the Sharepoint 12 hives log file and put it inside the database. This is the first time we decided to venture beyond what's provided by Microsoft API.

I'm creating a small console program. What's this console program did is, parsing 1 day of Site Usage Data directly from Sharepoint Log and save it inside the database. And we schedule to run it everyday at 1:30 Am. Sharepoint is always writing usage analysis to the log at 1:00 am on our central administration site. So we run our own Site Usage Parser at 1:30 am -- giving sharepoint enough time to finish writing the Site Usage Log to 12 hive.

And my console program is designed to read from 1 of the table called SiteCollections. It is the table where I defined which sites and subsites we wanted to log the user access. We designed this way because logging the whole of Sharepoint Server will make our database occupied alot of space. We designed to selectively log only those sites we wanted to monitor.

Hope you all fine my program useful. This will help you store the Site Usage inside database from Sharepoint log. And you will be able to store data selectively from the sites or subsites you want to monitor. As for front end presentation of how you want to show the usage to your users or auditor, I will leave it to your creativity. =)

Database Design

SiteCollection (Table)

Columns - URL(String) 'This is for specifying which site or subsites you want to parse the site usage. If you don't specify any site URL nothing will be parse from log file.

SiteUsageLog(Table) 'This is the main table where you will log all the parsed data from log file

Columns - LoginDate(DateTime) 'Store the date and time user log in
User_Name(String) 'domain\user
URL 'Site URL e.g http://yourmosssite/sites/yoursubsite
Referral 'Referral URL
Folder 'Which folder user access
Page 'Which page e.g default.aspx



MainConsole.vb

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Data.SqlClient
Imports Microsoft.SharePoint
Imports System.Collections
Imports System.Data
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports Microsoft.Win32
Imports System.Diagnostics


Namespace setDB4Log
Module MainConsole
Dim writer As StreamWriter
Dim logger As StreamWriter
Public sysLog As String = ""
'Public tracelog As String = ""
Dim dir As String = "D:\Log Files\SiteUsage\"
Dim logFile As String = dir & "SiteUsageConsole " & Now().ToString("yyyyMMdd_hhmmss") & ".log"
'Dim traceFile As String = dir & "TraceLog " & Now().ToString("yyyyMMdd_hhmmss") & ".log"
Sub Main(ByVal args As String())
'Connect String
'tracelog += Now().ToString("yyyyMMdd_hhmmss") & vbCrLf
Dim strConneectionSting As String = "Data Source=SQL2005;Initial Catalog=WSS_Usage_Log;User Id=User;Password=Password;"
Dim myconnection As New SqlConnection(strConneectionSting)

'Get log data for sites that are defined in SiteCollections table. Any other sites usage data won't be processed.
Dim strCmd As String = "SELECT URL from SiteCollections"
Dim adapter As New SqlDataAdapter(strCmd, myconnection)
Dim dtUrl As New DataTable()
adapter.Fill(dtUrl)


Dim parser As New Parser()
'fill in any url which belongs to your web application
Dim url As String = "http:\\yourmosssite"
Dim urlitem As String
Dim LogDir As String
Dim webappid As String
Dim [date] As String = ""
If args.Length = 0 Then
[date] = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd")
Else
For Each backDate As String In args
'To run back date logs
[date] = DateAndTime.DateValue(backDate).ToString("yyyy-MM-dd")
Next
End If
Console.WriteLine("Parsing Site Usage Data for " & [date])
'get yesterday
Dim insertRow As String
'Dim seekUrlList As String

'Delete data from database if the data is already exist in selected date
Dim deleterow As String = "DELETE from Site_Usage_Log where LoginDate >='" + [Date] + "' And LoginDate<'"+ [Date]+"'" 'Dim subUrl As String Dim insertCmd As New SqlCommand() Dim deleteCmd As New SqlCommand() Dim seekUrlCmd As New SqlCommand() Try 'get log file location Dim logRegkey As RegistryKey = Registry.LocalMachine logRegkey = logRegkey.OpenSubKey("SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\WSS") Dim val As Object = logRegkey.GetValue("LogDir") LogDir = val.ToString() Dim web As String Dim folder As String Dim item As String Dim doc As String Dim index As Integer 'Dim num As Integer Dim site As New SPSite(url) webappid = site.WebApplication.Id.ToString() LogDir = LogDir + webappid + "\" + [Date] + "\00.log" 'open the connection myconnection.Open() insertCmd.Connection = myconnection deleteCmd.Connection = myconnection seekUrlCmd.Connection = myconnection deleteCmd.CommandText = deleterow deleteCmd.ExecuteNonQuery() 'delete the overage date Dim ds As DataSet = parser.GetLogDataSet(LogDir) 'fill the database with the parsed data For Each row As DataRow In ds.Tables(0).Rows doc = row("Document").ToString() index = doc.LastIndexOf("/") web = row("Web").ToString() If (index < item =" doc" folder = "" folder =" web" item =" doc.Substring(index"> "") Then
urlitem = row("SiteUrl") + "/" + web
Else
urlitem = row("SiteUrl").ToString()
End If
'tracelog += urlitem & vbCrLf
'Console.WriteLine(urlitem)
For Each UrlRow As DataRow In dtUrl.Rows
If urlitem.ToLower().Contains(UrlRow(0).ToString().ToLower()) Then
If row("UserName").ToString() <> "sharepoint\system" Then
insertRow = "insert into Site_Usage_Log(LoginDate,User_Name,URL,Referral,Folder,Page) Values('" + [date] + " " + row("TimeStamp").ToString().Trim() + "','" + row("UserName").ToString().Trim() + "','" + urlitem + "','" + row("Referral").ToString().Trim() + "','" + folder.Trim() + "','" + item.Trim() + "')"
'tracelog += insertRow & vbCrLf
Console.WriteLine(insertRow)
insertCmd.CommandText = insertRow
insertCmd.ExecuteNonQuery()
End If
Exit For
End If
Next

Next
Catch ex As Exception
Console.WriteLine(ex.Message)
sysLog += ex.Message & vbCrLf
Finally
myconnection.Close()
End Try
If sysLog = "" Then
sysLog += "The log for " & [Date] & " is successfully created."
End If
Try
If Directory.Exists(dir) = False Then
Directory.CreateDirectory(dir)
End If
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try

writer = New StreamWriter(logFile)
writer.Write(sysLog)
writer.Close()
End Sub
End Module
End Namespace

Parser.vb -- This code does most of the parsing logic

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Data.SqlClient
Imports Microsoft.SharePoint
Imports System.Collections
Imports System.Data
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports Microsoft.Win32
Imports System.Diagnostics
Namespace setDB4Log
Public Class Parser
Private Const FILEHEADERLENGTH As Integer = 300
Private Const RECORDHEADERLENGTH As Integer = 50

Private _logRecords As New List(Of LogRecord)()
Private Sub ReadLogFile(ByVal logFilePath As String)
_logRecords.Clear()

' Open a stream to the log file
Using s As Stream = File.OpenRead(logFilePath)
Dim br As New BinaryReader(s)

' Read the file header
Dim fileHeaderData As Byte() = br.ReadBytes(FILEHEADERLENGTH)

While s.Position < s.Length
' Read the record header array
Dim recordHeader As Byte() = br.ReadBytes(RECORDHEADERLENGTH)
Dim headerData As New LogRecordHeader(recordHeader)

' Get the length of the current record and read the array
Dim recordLength As Integer = headerData.RecordLength()
Dim recordData As Byte() = br.ReadBytes(recordLength)
If recordData(0) = 0 Then
Exit While
End If
_logRecords.Add(New LogRecord(headerData, recordData))
s.Flush()
End While
s.Flush()
br.Close()
s.Close()
End Using
End Sub

Public Function GetLogDataSet(ByVal logFilePath As String) As DataSet
ReadLogFile(logFilePath)

Dim dsSTSLogs As New DataSet("dsSTSLogs")
Dim dtSTSLogs As New DataTable("dtSTSLogs")

Dim dcTimeStamp As New DataColumn("TimeStamp")
'DataColumn dcSiteGuid = new DataColumn("SiteGUID");
Dim dcSiteUrl As New DataColumn("SiteUrl")
Dim dcWeb As New DataColumn("Web")
Dim dcDocument As New DataColumn("Document")
Dim dcUserName As New DataColumn("UserName")
'DataColumn dcQueryString = new DataColumn("QueryString");
Dim dcReferral As New DataColumn("Referral")
'DataColumn dcUserAgent = new DataColumn("UserAgent");
'DataColumn dcCommand = new DataColumn("Command");
'DataColumn dcHttpStatus = new DataColumn("HttpStatus");
'DataColumn dcBytesSent = new DataColumn("BytesSent");

dtSTSLogs.Columns.Add(dcTimeStamp)
'dtSTSLogs.Columns.Add(dcSiteGuid);
dtSTSLogs.Columns.Add(dcSiteUrl)
dtSTSLogs.Columns.Add(dcWeb)
dtSTSLogs.Columns.Add(dcDocument)
dtSTSLogs.Columns.Add(dcUserName)
'dtSTSLogs.Columns.Add(dcQueryString);
dtSTSLogs.Columns.Add(dcReferral)
'dtSTSLogs.Columns.Add(dcUserAgent);
'dtSTSLogs.Columns.Add(dcCommand);
'dtSTSLogs.Columns.Add(dcHttpStatus);
'dtSTSLogs.Columns.Add(dcBytesSent);

dsSTSLogs.Tables.Add(dtSTSLogs)

For Each record As LogRecord In _logRecords
Dim drRecord As DataRow = dtSTSLogs.NewRow()

drRecord("TimeStamp") = record.TimeStamp
'drRecord["SiteGUID"] = record.SiteGUID;
drRecord("SiteUrl") = record.SiteUrl
drRecord("Web") = record.Web
drRecord("Document") = record.Document
drRecord("UserName") = record.UserName
'drRecord["QueryString"] = record.QueryString;
drRecord("Referral") = record.Referral
'drRecord["UserAgent"] = record.UserAgent;
'drRecord["Command"] = record.Command;
'drRecord["HttpStatus"] = record.Status;
'drRecord["BytesSent"] = record.BytesSent;

dtSTSLogs.Rows.Add(drRecord)
Next

Return dsSTSLogs
End Function
Public Sub ConvertToCSVFile(ByVal logFilePath As String)
Dim defaultPath As String = Path.Combine(Path.GetDirectoryName(logFilePath), Path.GetFileNameWithoutExtension(logFilePath)) + ".csv"

ConvertToCSVFile(logFilePath, defaultPath)
End Sub

Public Sub ConvertToCSVFile(ByVal logFilePath As String, ByVal csvFilePath As String)
ReadLogFile(logFilePath)

Dim recordLineFormat As String = "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11}"

Using sw As StreamWriter = File.CreateText(csvFilePath)
For Each record As LogRecord In _logRecords
sw.WriteLine([String].Format(recordLineFormat, record.TimeStamp, record.SiteGUID, record.SiteUrl, record.Web, record.Document, _
record.UserName, record.QueryString, record.Referral, record.UserAgent, record.Command, record.BytesSent, _
record.Status))
Next

sw.Close()
End Using
End Sub
End Class
Public Class LogRecordHeader
Public Const STANDARD_GUID_LENGTH As Integer = 36
Public Const STANDARD_TIMESTAMP_LENGTH As Integer = 8

Private _recordHeader As Byte()

Private _siteUrlLength As Integer
Public ReadOnly Property SiteUrlLength() As Integer
Get
Return _siteUrlLength
End Get
End Property

Private _webLength As Integer
Public ReadOnly Property WebLength() As Integer
Get
Return _webLength
End Get
End Property

Private _docLength As Integer
Public ReadOnly Property DocLength() As Integer
Get
Return _docLength
End Get
End Property

Private _userNameLength As Integer
Public ReadOnly Property UserNameLength() As Integer
Get
Return _userNameLength
End Get
End Property

Private _bytesSent As Long
Public ReadOnly Property BytesSent() As Long
Get
Return _bytesSent
End Get
End Property

Private _httpStatus As Integer
Public ReadOnly Property HttpStatus() As Integer
Get
Return _httpStatus
End Get
End Property

Private _queryStringLength As Integer
Public ReadOnly Property QueryStringLength() As Integer
Get
Return _queryStringLength
End Get
End Property

Private _referralLength As Integer
Public ReadOnly Property ReferralLength() As Integer
Get
Return _referralLength
End Get
End Property

Private _userAgentLength As Integer
Public ReadOnly Property UserAgentLength() As Integer
Get
Return _userAgentLength
End Get
End Property

Private _commandLength As Integer
Public ReadOnly Property CommandLength() As Integer
Get
Return _commandLength
End Get
End Property

Public Sub New(ByVal recordHeader As Byte())
Me._recordHeader = recordHeader
Me._siteUrlLength = BitConverter.ToUInt16(recordHeader, SITEURL_OFFSET)
Me._webLength = BitConverter.ToUInt16(recordHeader, WEB_OFFSET)
Me._docLength = BitConverter.ToUInt16(recordHeader, DOC_OFFSET)
Me._bytesSent = BitConverter.ToUInt32(recordHeader, BYTESSENT_OFFSET)
Me._httpStatus = BitConverter.ToUInt16(recordHeader, HTTPSTATUS_OFFSET)
Me._userNameLength = BitConverter.ToUInt16(recordHeader, USERNAME_OFFSET)
Me._queryStringLength = BitConverter.ToUInt16(recordHeader, QUERYSTRING_OFFSET)
Me._referralLength = BitConverter.ToUInt16(recordHeader, REFERRAL_OFFSET)
Me._userAgentLength = BitConverter.ToUInt16(recordHeader, USERAGENT_OFFSET)
Me._commandLength = BitConverter.ToUInt16(recordHeader, COMMAND_OFFSET)
End Sub

Private Const SITEURL_OFFSET As Integer = 12
Private Const WEB_OFFSET As Integer = 14
Private Const DOC_OFFSET As Integer = 16
Private Const BYTESSENT_OFFSET As Integer = 20
Private Const HTTPSTATUS_OFFSET As Integer = 24
Private Const USERNAME_OFFSET As Integer = 26
Private Const QUERYSTRING_OFFSET As Integer = 28
Private Const REFERRAL_OFFSET As Integer = 30
Private Const USERAGENT_OFFSET As Integer = 32
Private Const COMMAND_OFFSET As Integer = 34

Public Function RecordLength() As Integer
Return STANDARD_GUID_LENGTH + 1 + STANDARD_TIMESTAMP_LENGTH + 1 + SiteUrlLength + 1 + WebLength + 1 + DocLength + 1 + UserNameLength + 1 + QueryStringLength + 1 + ReferralLength + 1 + UserAgentLength + 1 + STANDARD_GUID_LENGTH + 1 + CommandLength + 1
End Function
End Class

Public Class LogRecord
Private _siteGuid As String
Public ReadOnly Property SiteGUID() As String
Get
Return _siteGuid
End Get
End Property

Private _timeStamp As String
Public ReadOnly Property TimeStamp() As String
Get
Return _timeStamp
End Get
End Property

Private _siteUrl As String
Public ReadOnly Property SiteUrl() As String
Get
Return _siteUrl
End Get
End Property

Private _web As String
Public ReadOnly Property Web() As String
Get
Return _web
End Get
End Property

Private _document As String
Public ReadOnly Property Document() As String
Get
Return _document
End Get
End Property

Private _userName As String
Public ReadOnly Property UserName() As String
Get
Return _userName
End Get
End Property

Private _queryString As String
Public ReadOnly Property QueryString() As String
Get
Return _queryString
End Get
End Property

Private _referral As String
Public ReadOnly Property Referral() As String
Get
Return _referral
End Get
End Property

Private _userAgent As String
Public ReadOnly Property UserAgent() As String
Get
Return _userAgent
End Get
End Property

Private _command As String
Public ReadOnly Property Command() As String
Get
Return _command
End Get
End Property

Private _status As Integer
Public ReadOnly Property Status() As Integer
Get
Return _status
End Get
End Property

Private _bytesSent As Long
Public ReadOnly Property BytesSent() As Long
Get
Return _bytesSent
End Get
End Property

Public Sub New(ByVal headerData As LogRecordHeader, ByVal recordData As Byte())
Dim timeStampOffset As Integer = LogRecordHeader.STANDARD_GUID_LENGTH + 1
Dim siteUrlOffset As Integer = timeStampOffset + LogRecordHeader.STANDARD_TIMESTAMP_LENGTH + 1
Dim webOffset As Integer = siteUrlOffset + headerData.SiteUrlLength + 1
Dim documentOffset As Integer = webOffset + headerData.WebLength + 1
Dim userNameOffset As Integer = documentOffset + headerData.DocLength + 1
Dim queryStringOffset As Integer = userNameOffset + headerData.UserNameLength + 1
Dim referralOffset As Integer = queryStringOffset + headerData.QueryStringLength + 1
Dim userAgentOffset As Integer = referralOffset + headerData.ReferralLength + 1
Dim commandOffset As Integer = userAgentOffset + headerData.UserAgentLength + LogRecordHeader.STANDARD_GUID_LENGTH + 2

Dim enc As Encoding = UTF8Encoding.[Default]

_siteGuid = enc.GetString(recordData, 0, LogRecordHeader.STANDARD_GUID_LENGTH)
_timeStamp = enc.GetString(recordData, timeStampOffset, LogRecordHeader.STANDARD_TIMESTAMP_LENGTH)
_siteUrl = enc.GetString(recordData, siteUrlOffset, headerData.SiteUrlLength)
_web = enc.GetString(recordData, webOffset, headerData.WebLength)
_document = enc.GetString(recordData, documentOffset, headerData.DocLength)
_userName = enc.GetString(recordData, userNameOffset, headerData.UserNameLength)
_queryString = enc.GetString(recordData, queryStringOffset, headerData.QueryStringLength)
_referral = enc.GetString(recordData, referralOffset, headerData.ReferralLength)
_userAgent = enc.GetString(recordData, userAgentOffset, headerData.UserAgentLength)
_command = enc.GetString(recordData, commandOffset, headerData.CommandLength)
_status = headerData.HttpStatus
_bytesSent = headerData.BytesSent
End Sub
End Class
End Namespace

My first Technical Blog

Hi,

My name is Leo. I'm from Singapore. And my day to day job focus is on Sharepoint 2007 and VB.NET. It's been quite awhile I'm pondering the thoughts of setting up a technical blog to share some of my technical knowledge with fellow programmers around the world. But I've too little time to do so due to many commitments. Anyway, I guess time to put my excuses aside and started blogging some entries.

I'll blog from some short and simple coding to lengthy technical article depending on things that I want to share. Hope you all find this technical blog useful. Drop me comments if you have any questions. If I have the answer, I will get back to you. :)