Preston L. Bannister { random memes }

2007.06.20

Must be upgrade time…

Filed under: Personal — Preston @ 8:56 pm

For rather a while I have not felt especially motivated to update my computers. Even my old Athlon 2200+ laptop is adequate for almost everything I do. With Eclipse, VMware, Tomcat, and Firefox running – I tend to want more memory rather than more CPU.

Until today. Wanted to record a video of a short demo. Set a Windows 2000 VM under VMware to 1152×864, and used VMware Workstation’s “Capture Movie” to record the demo. Copied the AVI file to my laptop, fired up Window XP’s Movie Editor and … boy oh boy could I use a lot more CPU!

Still mucking around trying to figure out how to convert the video (encoded with the VMware VNC-derived codec) into more easily distributable form (at full resolution, without requiring the viewer to add the VMware codec. Each experiment takes …….. a long time with the CPU running flat out.

2007.06.17

Hijacked?

Filed under: Web — Preston @ 9:25 am

Does it bug anyone else when a Google search returns a hit for a site like “readablog” before the hit for your original article?

Clearly they have figured out how to game the search engines (or Google at least). Clearly their aim is to sell advertising (as “view source” shows) using weblogs as “free” content.

I do not mind aggregators (like Bloglines or Google Reader) in the slightest as they offer added value to the user. I do mind sites like “readablog” that offer no added value, and want to profit off a mangled copy of my content by presenting it to search engines.

2007.06.15

50-year-old car unburied

Filed under: Humor, Personal — Preston @ 8:17 am



Tulsa World

This car was buried the same year – and not far from – where and when I was born. In this field that makes me ancient (something my teenagers will confirm).

Caught in the DreamHost(?) security breach

Filed under: Security, Web — Preston @ 5:58 am

Seems my account is caught in the DreamHost security breach. Checked my pages (via “View Source” in the browser) after the first announcement from DreamHost, did not see any junk, and let things alone. Since DreamHost claimed to be notifying customers whose accounts were compromised, and I had seen nothing from DreamHost (and still have not), I assumed (for the moment) that I was not affected.

Wrong. Last night noticed some junk in one of my pages – hidden via “display:none” – similar to this report.

On 2007-06-13 between 15:31 and 15:35 someone ran a script and altered the index.html and index.php files on my site. Last night I backed up the entire site, then edited out the junk. Filed a support request reporting the breach. I did not change my FTP password (intentionally!) to see if the breach would be repeated.

It was. Checked again early this morning. On 2007-06-15 at 04:42 someone again altered index.html and index.php files (a smaller number this time). Changed my FTP password, backed up the changed files, and removed the junk.

Digging a little deeper, ran “last -ad myusername | grep -v myhomeip“, and got:

xxxxxxxx ftpd24994    Fri Jun 15 04:42 - 04:43  (00:00)     venus.websiteactive.com
xxxxxxxx ftpd28128    Fri Jun 15 01:28 - 01:28  (00:00)     venus.websiteactive.com
xxxxxxxx ftpd4197     Wed Jun 13 15:25 - 15:35  (00:09)     neptune.pronameservice.net
xxxxxxxx ftpd30356    Tue Jun 12 15:32 - 15:32  (00:00)     hv111.steephost.com
xxxxxxxx ftpd555      Sun Jun 10 09:41 - 09:41  (00:00)     gator188.hostgator.com
xxxxxxxx ftpd5386     Sat Jun  9 10:45 - 10:45  (00:00)     85.222.183.212
xxxxxxxx ftpd2599     Sat Jun  9 10:45 - 10:45  (00:00)     85.222.183.212
xxxxxxxx ftpd8498     Sat Jun  9 10:45 - 10:45  (00:00)     85.222.183.212
xxxxxxxx ftpd20735    Sat Jun  9 10:45 - 10:45  (00:00)     85.222.183.212
xxxxxxxx ftpd24834    Sat Jun  9 10:45 - 10:45  (00:00)     85.222.183.212
xxxxxxxx ftpd19653    Sat Jun  9 10:44 - 10:44  (00:00)     85.222.183.212
xxxxxxxx ftpd15132    Sat Jun  9 10:44 - 10:44  (00:00)     85.222.183.212
xxxxxxxx ftpd10851    Sat Jun  9 10:44 - 10:44  (00:00)     85.222.183.212
xxxxxxxx ftpd10095    Sat Jun  9 10:44 - 10:44  (00:00)     85.222.183.212
xxxxxxxx ftpd14762    Sat Jun  9 10:44 - 10:44  (00:00)     85.222.183.212
xxxxxxxx ftpd32292    Fri Jun  8 15:20 - 15:20  (00:00)     gator188.hostgator.com
xxxxxxxx ftpd25266    Fri Jun  8 05:59 - 06:00  (00:00)     219.83.97.23
xxxxxxxx ftpd26076    Fri Jun  8 05:59 - 05:59  (00:00)     219.83.97.23
xxxxxxxx ftpd2712     Fri Jun  8 05:59 - 05:59  (00:00)     219.83.97.23
xxxxxxxx ftpd10378    Fri Jun  8 05:59 - 05:59  (00:00)     219.83.97.23
xxxxxxxx ftpd8738     Fri Jun  8 05:59 - 05:59  (00:00)     219.83.97.23
xxxxxxxx ftpd15500    Fri Jun  8 05:59 - 05:59  (00:00)     219.83.97.23
xxxxxxxx ftpd741      Fri Jun  8 05:58 - 05:58  (00:00)     219.83.97.23
xxxxxxxx ftpd9567     Fri Jun  8 05:58 - 05:58  (00:00)     219.83.97.23
xxxxxxxx ftpd31931    Fri Jun  8 05:58 - 05:58  (00:00)     219.83.97.23
xxxxxxxx ftpd17539    Fri Jun  8 05:58 - 05:58  (00:00)     219.83.97.23
xxxxxxxx ftpd17       Fri Jun  8 05:58 - 05:58  (00:00)     219.83.97.23
xxxxxxxx ftpd20698    Fri Jun  8 05:57 - 05:58  (00:00)     219.83.97.23
xxxxxxxx ftpd4417     Fri Jun  8 05:57 - 05:57  (00:00)     219.83.97.23
xxxxxxxx ftpd32161    Fri Jun  8 05:57 - 05:57  (00:00)     219.83.97.23
xxxxxxxx ftpd24593    Fri Jun  8 05:57 - 05:57  (00:00)     219.83.97.23
xxxxxxxx ftpd3318     Fri Jun  8 05:57 - 05:57  (00:00)     219.83.97.23
xxxxxxxx ftpd2887     Fri Jun  8 05:57 - 05:57  (00:00)     219.83.97.23
xxxxxxxx ftpd10251    Fri Jun  8 05:56 - 05:57  (00:00)     219.83.97.23
xxxxxxxx ftpd28277    Fri Jun  8 05:56 - 05:56  (00:00)     219.83.97.23
xxxxxxxx ftpd6156     Fri Jun  8 05:56 - 05:56  (00:00)     219.83.97.23
xxxxxxxx ftpd4639     Fri Jun  8 05:56 - 05:56  (00:00)     219.83.97.23
xxxxxxxx ftpd14610    Fri Jun  8 05:56 - 05:56  (00:00)     219.83.97.23
xxxxxxxx ftpd7016     Fri Jun  8 05:56 - 05:56  (00:00)     219.83.97.23
xxxxxxxx ftpd31218    Fri Jun  8 05:55 - 05:56  (00:00)     219.83.97.23
xxxxxxxx ftpd15428    Fri Jun  8 05:55 - 05:55  (00:00)     219.83.97.23
xxxxxxxx ftpd23894    Fri Jun  8 05:55 - 05:55  (00:00)     219.83.97.23
xxxxxxxx ftpd12686    Fri Jun  8 05:55 - 05:55  (00:00)     219.83.97.23
xxxxxxxx ftpd26025    Fri Jun  8 05:55 - 05:55  (00:00)     219.83.97.23
xxxxxxxx ftpd32523    Fri Jun  8 05:55 - 05:55  (00:00)     219.83.97.23
xxxxxxxx ftpd22799    Fri Jun  8 05:55 - 05:55  (00:00)     219.83.97.23
xxxxxxxx ftpd17447    Fri Jun  8 05:54 - 05:55  (00:00)     219.83.97.23
xxxxxxxx ftpd20174    Thu Jun  7 14:37 - 14:37  (00:00)     hv111.steephost.com
xxxxxxxx ftpd29543    Mon Jun  4 04:33 - 04:33  (00:00)     sv25.xserverzero.net
xxxxxxxx ftpd19850    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19848    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19843    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19840    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19836    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19833    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19830    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19829    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19827    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19825    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19821    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19819    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19812    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19809    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19805    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19804    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19798    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19797    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19792    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19790    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19788    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19787    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19785    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19782    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19781    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19779    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19775    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19774    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19772    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info
xxxxxxxx ftpd19770    Sun Jun  3 22:20 - 22:20  (00:00)     server.site60.info

So it does look like the access is via FTP, using subverted sites, and has been ongoing for at least a couple weeks (the last log only goes back that far). This also tells us that the DreamHost folk are at somewhat less than guru status, as if in their shoes I’d have written scripts to look for this sort of pattern, notified customers, and (possibly) blocked access from known subverted sites. For someone of strong ability, this is not hard.

In fact I am more amused than bothered by this. All my stuff is backed up, and I never assumed a webhosting account would be secure (at least in any strong sense). I expect most webhost outfits are similar (or worse). I am slightly bothered that they don’t seem to have access to at least one really sharp guy … but not surprised.

So … with proof on ongoing access, and after changing my FTP password, by tomorrow we should know whether this was a one-time breach of the FTP password list, or something deeper.

BTW – a hint for fixing the changed files (tedious but not difficult) from the shell.

vi `find sitedirectory -type f -newer fileslightlyolderthanhack`

Of course, if you have a really large number of changed files, you want to restore from backup, or write a Perl script.

Update 2007.06.16: Changing the password on the one account with FTP access to the website files (on Friday morning) seems to be all that was needed. Did get a reasonable response (and another response to my reply) from DreamHost well within 24 hours on filing a support request. I am satisfied with their response, though I stand by what I said above.

2007.06.14

Mapping components – Javascript

Filed under: Javascript, Software — Preston @ 1:48 pm

Accessing components in the DOM from Javascript using getElementById() is tedious, somewhat verbose, and does nothing to enhance the readability of the code. Generally there are two cases: finding global elements within the page, and finding members within (possibly repeated) components.

There are (of course) many ways to do this, but what I have settled on is using the ID attribute to identify global elements (what ID was meant for), and using the NAME attribute to identify members within a component. This use of the NAME attribute is exactly analogous to standard usage inside forms, just extended for use with non-form objects.

The code to build a map of global objects:

DOM.byId = function() {
    var map = {};
    DOM.asArray(arguments).apply(function(name){
        var v = name.split('.');
        var o = map;
        while (1 < v.length) {
            var s = v.shift();
            o = o[s] = o[s] || {};
        }
        o[v[0]] = document.getElementById(name);
    });
    return map;
};

The code to build a map of a component:

DOM.byName = function(root) {
    var map = {};
    var walk = function(o) {
        var name = o.getAttribute && o.getAttribute('name');
        if (name) {
            var v = name.split('.');
            var m = map;
            while (1 < v.length) {
                var s = v.shift();
                m = m[s] || (m[s] = {});
            }
            m[v[0]] = o;
        }
        for (o = o.firstChild; o; o = o.nextSibling) {
            walk(o);
        }
    };
    walk(root);
    return map;
};

Usage is quite simple:

var named = DOM.byId(
    'grid1',
    'textWidth',
    'textHeight'
);
var grid1 = DOM.byName(named.grid1);

Now named (by ID) global objects in the DOM can be later referred to in the Javascript code simply as named.grid1, named.textWidth, named.textHeight. Named objects within a component (by NAME) can be referred to as grid1.container, grid1.top.container, grid1.top.strip, (etc.).

Note that both DOM.byId() and DOM.byName() map IDs and NAMEs like “foo.bar” into a corresponding structure:

{ foo: { bar: reference to DOM element } }

Code in base.js.

Auto-adjusting sizer – Javascript

Filed under: Javascript, Software — Preston @ 1:22 pm

Took a couple years to get reasonably fluent at HTML/CSS/Javascript, and pick out what seems a decent programming model. Reached the point where most of the public examples of Javascript usage (in particular) look to me as very poorly written. Still it seems like every time I turn out another example, I find another useful idiom.

Take sizing of DOM elements. Say you want to put a sizable component within a page. The DOM model for this is a mess. You can set the width and height of an element o with o.style.width and o.style.height (assuming the element is sizable). You can read the width and height of an element with o.offsetWidth and o.offsetHeight. Not exactly symmetric, and in fact – due to the DOM layout model – these are in fact often not quite the same numbers. Yuck.

Looking at a bunch of repetitive code (button handlers to alter a component’s width/height), realized there was an common idiom that could be extracted into a “sizer” object.

grid.sizer = function() {
    var o = { dx: grid1.container.offsetWidth, dy: grid1.container.offsetHeight };
    o.apply = function() {
        grid1.container.style.width  = o.dx + "px";
        grid1.container.style.height = o.dy + "px";
        o.dx = grid1.container.offsetWidth;
        o.dy = grid1.container.offsetHeight;
    }
    return o;
};

(The component has been mapped by functions DOM.byId() and DOM.byName() described here.)

Now either width, height, or both can be set or adjusted using the “sizer”.

sizer.dx = 500;
sizer.apply();
sizer.dx += 100;
sizer.dy = 400;
sizer.apply();

Kind’a like a remote control for a component. But still not quite right, as (depending on the CSS) the actual component size may be a little different than what was set via the style. The difference is not readily deducible from the DOM, and I did not want to kludge in anything browser or style specific. The difference between the actual size and the size wanted can be computed … then it occurred to me that the “sizer” could be made auto-adjusting.

grid.sizer = function() {
    var ddx = 0, ddy = 0;
    var o = { dx: grid1.container.offsetWidth, dy: grid1.container.offsetHeight };
    o.apply = function() {
        grid1.container.style.width  = (ddx + (0 | o.dx)) + "px";
        grid1.container.style.height = (ddy + (0 | o.dy)) + "px";
        var now = { dx: grid1.container.offsetWidth, dy: grid1.container.offsetHeight };
        ddx += o.dx - now.dx;
        ddy += o.dy - now.dy;
        o.dx = now.dx;
        o.dy = now.dy;
    }
    o.adjust = function() {
        var want = grid.sizer();
        o.apply();
        o.dx = want.dx;
        o.dy = want.dy;
        o.apply();
    }
    return o;
};
var sizer = grid.sizer();
sizer.adjust();
grid.layout();

The essential bit here is that the “sizer” remembers the difference between the size as set, and the actual size in ddx and ddy. A single upfront call to sizer.adjust() initializes ddx and ddy. Note that on later changes to the component size, if the actual size differs from what was wanted (due to CSS changes, browsers bugs, whatever), then ddx and ddy are adjusted to match.

Simple, clean, and browser-independent.

2007.06.12

Safari on Windows!

Filed under: Software — Preston @ 9:05 am

Generally I test web applications on Firefox (Windows, Linux) and IE (Windows). On (rare) occasion, I have tested on Opera, but … it is hard to care about Opera. My one regret is that up until now I have not been able to test with Safari. Personally, I would like to know the things I create will work on the Macintosh, but this is not a priority in my work-time (hence acquiring a Macintosh is hard to justify).

Yesterday I saw the announcement for the Safari 3 Public Beta, and there is a Windows version! Very cool. Grabbed the Windows version and ran a few examples through Safari, and … some did not work, and I cannot easily tell why. Poked around the Apple developer site a bit, but lacking something like Firebug, I probably will not take the time needed to puzzle out what is going wrong.

Kind of funny, as before Firebug I had code for in-page Javascript logging (for both IE and Firefox) that was essential when testing Javascript code. Given Firebug (and the lite version for IE), is equal or (much!) better, it made sense to chuck what I had written. Now, faced with a similar need in Safari, I just do not want to spend the time.

Hopefully the developer tools for Safari on Windows will eventually appear, and I can take another crack later.

Update: A note has appeared on Enabling the debug menu on Safari for Windows. Enough of a hint to find why the parameters example did not work (that and <html debug=”true”> does something useful with Firebug Lite). Fixed.

2007.06.10

Storing data within a web page – Javascript

Filed under: Javascript, Software — Preston @ 11:22 pm

An approach or two to storing data within a web page. Something like “microformats”, only not. First approach, embed JSON data within an updatable SCRIPT tag, with a test page. Works in Firefox, but in IE when updating the tag containing the data, the script is executed (which is arguably right – if inconvenient in this case). Something like:

<script id="properties" type="text/javascript">
PAGE.add({"age":123,"revision":345,"editor":"John Doe"});
</script>

Second approach, embed JSON data within a hidden, updatable tag, with a test page. Works in both Firefox and IE (no surprise).  Something like:

<div id="properties" style="display: none;">[{"age":123,"revision":345,"editor":"John Doe"}]</div>

Requires an extra bit of encoding, but nothing difficult. The result is arguably cleaner.

var readProperties = function() {
    PAGE.data = JSON.parseJSON(DOM.decode(named.properties.innerHTML)) || [];
};
var injectProperties = function() {
    named.properties.innerHTML = DOM.encode(JSON.toJSON(PAGE.data));
};

This is leading up to something else altogether.

Next Page »