Friday, July 31, 2009

Unlocking PureEdge forms

I was creating an inprocessing web application. It had to serve up many official legal forms. So I couldn't mess with their layout or content much without making them invalid. One of the forms was a locked XFDL PureEdge form. I really needed to get some hardcoded data off the form. The form was base64 encoded and had a lock protection thing set on it.

A normal base64 decoder wouldn't work because of some unicode character problems so I wrote a custom decoder. Once the form was decoded I found the text on and switched it to off and the form was unlocked. Now I could modify it using the pureEdge developer.

This was around a year ago. I've been digging through my emails trying to find all the specific details so I can log them here for myself and for others but I can't find it. So for now I will just write this until I have to re-write the custom decoder later and can better document the steps taken (many frustrating days of work).

GlassFish IIS passthrough SSO

JCIFS only does NTLMv1. Our security policies here dictate that only NTLMv2 should be used. JCIFS does NTLMv1 through a man-in-the-middle way of passing the authentication request from the browser to the web server then to the active directory controller then back.

There is documentation about this at: http://jcifs.samba.org/src/docs/ntlmhttpauth.html

One solution is to put IIS in front of GlassFish to handle the Single Sign On.

So I setup an IIS server with the isapi sun-passthrough.dll with integrated security enabled. I pointed it to our development server stand-alone (no cluster, no load balancer, no node agents). Request.getRemoteUser() isn't being populated but I am able to pull the authenticated username by base64 decoding the NTLM type 3 message in request.getHeader("Authorization") that is passed along from IIS.

I don't want to do this in every web application though. So a better solution is needed.

Google Voice, VOIP, SIP, MagicJack, Gizmo, XLite, WRTP54G

Google Voice will only allow you to do PSTN to PSTN (public switched telephone network) calls. There is no way to dial out from a computer or directly receive calls on a computer. I am moving to Japan and will need to call back to the US often.

There are a few services that provide PSTN dial in to SIP forwarding. Ipkall and Gizmo offer free dial in to SIP phone numbers. Gizmo makes money on outgoing phone calls. IpKall.. doesn't make money and only have dial in lines in washington.

GV does offer forwarding to PSTN phone numbers or Gizmo SIP accounts. So I can use my GV California number forwarded to Gizmo SIP and answer on my computer. I can't call out though.

A good friend gave me his unused Magic Jack. MJ is a USB to PSTN (RJ11) device plus unlimited service for a year for $40. Having a windows computer running 24/7 to be able to receive phone calls on MJ is annoying. If any computer was going to be running 24/7 at my house it wouldn't be running windows, it would be running Linux. MJ doesn't support Linux. Having a usb dongle always connected is slightly annoying. MJ forces advertising onto the screen when calls are made or received. Annoying.

So I ran some software to export the MJ SIP account information. Gizmo wouldn't allow me to put that SIP info in. Gizmo software only seems to support it's own SIP service.

I put the MJ SIP info into XLite and it didn't work. I changed the settings 20 times. Then it did suddenly work. I closed it and reopened it. No go. Changed the settings 20 times and suddenly it worked again. No settings seemed to work right away. Eventually I figured out that the first connect was failing but changing settings forced a re-attempt. I'll go more into details on that later. I found that the older X-Lite 2.0 didn't have this problem. So I got that setup on Linux and Windows.

Now I can dial out and receive calls on the computer from anywhere yeahhh! Now I want to hook up a wireless phone. I can't use the MJ hardware anymore. There are some old undocumented drivers to get MJ hardware working with XLite but I couldn't get them to work.

So I ordered a WRTP54G-ER Wireless Router w/ Earthlink TrueVoice for $30! Wifi and VOIP SIP in one router with VOIP priority. These things sell for around $200 but noone wants to use EarthLink VOIP. The device is locked to EarthLink. So I will have to hack it and make it use the MJ SIP account information. I've been waiting for 3 weeks for it to show up. More info on that when it shows up.

I seem to do everything the hard way to save a few bucks. I enjoy most of the process but in the end it sometimes ends up not being worth it. This blog helps add some value to my hard work. Maybe someday someone will read it.

Java GlassFish JCIFS access logging NULL-AUTH-USER

If you are using JCIFS for java web application SSO authentication in GlassFish you will not get usernames logged in the access log.

I posted this at: http://forums.java.net/jive/thread.jspa?messageID=358412 but this solution took 7 hours to develop and should help some other people so I am reposting it here.

I ran into the same problem. We are using jcifs.http.NtlmHttpFilter for authentication. The access logs show NULL-AUTH-USER. I saw your solution, copying the remoteUser into request scope in every application and logging using %attribute.%. We have around 20 web apps though. So this did not seem like a reliable solution. I considered modifying jcifs source but jcifs is a library in every webapp currently, so all would need to be modified. I found that JCIFS puts the username in a session attribute called NtlmHttpAuth. Glassfish doesn't have any documented method to log session variables. So I searched through the glassfish source code for any undocumented features. No session logging.

I found that with adding one line of code to glassfish I could make JCIFS authenticated user access logging work.

I added:

if (user == null && hreq.getSession(false) != null && hreq.getSession(false).getAttribute("NtlmHttpAuth") != null) { user =
hreq.getSession(false).getAttribute("NtlmHttpAuth").toString(); }

in com\sun\enterprise\web\accesslog\DefaultAccessLogFormatterImpl.java appendAuthUserName method (line 388).

I compiled it and put it in:
C:\Sun\AppServer\lib\appserv-rt.jar

We are running Sun GlassFish Enterprise Server v2.1 (9.1.1). I only compiled and added that one java file to appserv-rt.jar. I used source from:
GlassFish Project - v2.1 FinalBuild (also known as v2.1 b60e Promoted Build).

This isn't the best way to patch glassfish but it is working.

Wednesday, July 8, 2009

Google Voice

Ok this isn't coding related. Many of my posts are more about hacking things than coding specifically.

I just got my google voice invite today! This is perfect. Hopefully I can port my cell number to it soon because I will be moving to Japan in under 2 months and need some kind of VOIP solution and a place to port my cell phone numbers to.

I'll post again when I've actually played with google voice. I'm at work right now. I signed up many many months ago soon after google took over from grandcentral.

Thursday, July 2, 2009

ROT 13 encoding and computer basics for an 8 year old


I showed my daughter how to write secret messages using ROT13.

If you haven't heard of ROT 13, imagine cutting an empty toilet roll in half
(vertically, so you still get 2 cylinders).
You write: ABCDEFGHIJKLMNOPQRSTUVWXYZ on one.
Then: ABCDEFGHIJKLMNOPQRSTUVWXYZ on the other.
Line up A on the top one with A on the bottom one.
ROT is short for rotate.
Now rotate the bottom cylinder 13 letters ahead so that A is above and N is below.
Flat on paper this now looks like:
ABCDEFGHIJKLMNOPQRSTUVWXYZ.
NOPQRSTUVWXYZABCDEFGHIJKLM.
You can now us this to encode and decode messages.
HELLO would become:
URYYB
http://en.wikipedia.org/wiki/Rot13

I also showed her letters to numbers:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26.

So here HELLO would become:
8 5 12 12 15.

Next I'll teach her that computers have character maps. ASCII.
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90.

Hold down ALT and press 72 and you get H.
ALT 72 ALT 69 ALT 76 ALT 76 ALT 79 would become:
HELLO

Eventually I'll explain binary and how data is stored using electrical ons and offs.

.NET gridview not showing header is no data

I've been fighting with empty girdviews not showing the header for a while and this is the solution I came up with:


public static void gridViewShowHeader(GridView gridView)
{
// Show header row (if none)
Table table = new Table();
if (gridView.Controls.Count != 0) table = (Table)gridView.Controls[0];
if (table.Rows.Count != 0 && ((GridViewRow)table.Rows[0]).RowType
== DataControlRowType.Header) return;
GridViewRow headerRow = new GridViewRow(-1, -1,
DataControlRowType.Header, DataControlRowState.Normal);
for (int i = 0; i < gridView.Columns.Count; i++)
{
TableHeaderCell headerCell = new TableHeaderCell();
headerCell.Text = gridView.Columns[i].HeaderText;
headerRow.Controls.Add(headerCell);
}
table.Rows.AddAt(0, headerRow);
gridView.Controls.Add(table);
}



I have another routine that adds dropdown lists as column filters.
So this routine doesn't just test for an empty gridview, it tests
for a gridview without a header. So this can be run before or after
my gridViewShowFilters routine without a problem.

.NET asp:BoundField DataFormatString problem


To apply a DataFormatString on a BoundField like DataFormatString="{0:MM/dd/yyyy}
in older versions of .NET you need to set HtmlEncode="false". It doesn't seem to
be needed in 3.5. If you are going to allow editing also add
ApplyFormatInEditMode="true".

I decided to do this programmaticly because I am dealing with many reports (~8)
with lots of fields (28-53) that change often. There is one method for gridViews
and another for detailsView.

public static void gridViewFormatDates(GridView gv)
{
for (int i = 0; i < gv.Columns.Count; i++)
{
if (gv.Columns[i].GetType() == typeof(BoundField))
{
BoundField d = (BoundField)gv.Columns[i];
SqlDataSource sds = (SqlDataSource) gv.DataSourceObject;
if (gv.Columns[i].SortExpression.Contains("date"))
{
d.ApplyFormatInEditMode = true;
d.DataFormatString = "{0:MM/dd/yyyy}";
}
}
}
}

public static void detailsViewFormatDates(DetailsView dv)
{
for (int i = 0; i < dv.Fields.Count; i++)
{
if (dv.Fields[i].GetType() == typeof(BoundField))
{
BoundField d = (BoundField)dv.Fields[i];
if (dv.Fields[i].SortExpression.Contains("date"))
{
d.ApplyFormatInEditMode = true;
d.DataFormatString = "{0:MM/dd/yyyy}";
}
}
}
}

Ok you may have noticed that the fieldname (sortExpression) must have "date"
in it for this to work. Yeah it's ugly. So I came up with another solution
which still isn't perfect either.

I previously created a method to shorten long gridview columns. So I added
this automatic date formatting to it. Call this from your onrowcreated method
like gridViewShortenColumns(myGridView, e.Row, 30);

public static void gridViewShortenColumns(GridView gv, GridViewRow gvr, int width)
{
DataRowView dr = (DataRowView)gvr.DataItem;
for (int i = 0; i < gvr.Cells.Count; i++)
{
DataControlFieldCell d = (DataControlFieldCell)gvr.Cells[i];
String name = d.ContainingField.SortExpression;
if (!name.Equals("") && dr[name].GetType() == typeof(DateTime))
((BoundField)gv.Columns[i]).DataFormatString = "{0:MM/dd/yyyy}";
if (dr != null && name != null && !name.Equals("") &&
gvr != null && dr[name] != null && dr[name].ToString().Length > width)
{
gvr.Cells[i].ToolTip = dr[name].ToString();
dr[name] = dr[name].ToString().Substring(0, width) + " ...";
}
}
}