Categories
Technology Weblogging

Comment spam quick fix

Recovered from the Wayback Machine.

Both Sam Ruby and Phil Ringnalda had good advice — don’t spend a lot of time on developing a solution to fixing the comment spam problem. Whatever I can do within the form, it’s a relatively simple matter for a spammer to read any form value and duplicate it in his spam blast.

I appreciate both their help in gently pointing out that I was spinning my wheels (but I have to get practice for ice driving).

So, here’s a quick fix — it will keep out the lightweights at least. It’s a start as other efforts are underway.

This approach will require you modifying the following MT templates:

Individual data entry
Comment Listing Template
Comment Preview Template
Comment Error Page

You’ll be adding the following field, on the line before the </form> tag:

 

<input type=”hidden” name=”snoop” value=”goaway” />

 

You can change both the name and the value field, as long as you’re consistent with the name throughout the templates and the code.

Next, open your mt-comments.cgi (or mt-comments.pl) file and add the following code just after the “use strict;” line:

 

use CGI qw(:standard);

if ($ENV{‘REQUEST_METHOD’} eq “POST”) {

my $data = param(‘snoop’);

die unless ($data);
}

 

Most everyone should have the CGI.pm perl module installed. Make sure to change ‘snoop’ to whatever your little secret field is (let’s all use different fields, make the spammer’s job a little tiny bit harder.

That’s it.

What happens is that when you post a comment, the code checks for a form field of “snoop”. If it doesn’t find it, it dies. Nothing fancy at all. This will show in your error log or web log file as a premature end to the script. It doesn’t prevent others from using the application, and doesn’t crash anything.

Again, this isn’t fancy, but it’s a start. Holler if you have questions. If you’re uncomfortable modifying mt-comments, let me know and I’ll help you. If you have a better solution, or see problems with mine, please let me know.

Again — thanks to Phil and Sam for advice, help, suggestions.

Update:

Mark has put together a nice re-cap on the whole comment spamming thing. What I just created is a ‘club’. I’m going in for an interview tomorrow and when they ask me what was the last application I worked on, I’ll answer “A club”. .

Categories
Technology Weblogging

Comment spam problem continued

Recovered from the Wayback Machine.

In regards to the comment spam problem mentioned earlier, one idea kicked around was checking the http_referer to make sure that the comment post came from the same server as the form.

We talked about the possibility of empty http_referers — not all browsers send a referrer and proxy servers can strip out the referrer. The solution would be to allow empty referrers in addition to referrers from the server. Unfortunately, though, allowing for empty http_referers will also allow in the comment spammer.

The reason why allowing empty referers opens the door to the spammer is the comment spamming code would invoke my comment code directly, not through a link from an HTML page. In this case http_referer would be empty.

I could become more restrictive, remove the permission for empty referrer, but if I do, I won’t be letting some of you through (as you’ve been kind enough to let me know via email tonight).

Sam Ruby had some good ideas such as putting hidden form fields into the comment forms and testing for these and this will be a next step. This means adding form fields to all templates related to comments, and then adding code to mt-comments.cgi. Doable, and many appreciations to Mr. Ruby for excellent ideas. (If you don’t know Sam, he works on some weird sounding things such as “Comanche” and “SOUP” — stuff like that).

A really nifty and difficult to crack approach (IMO) would be to take the person’s login name and the comment id for each comment and use these to create an encrypted value. Stuff this into an HTML form field. When the form is processed, test to see if the encrypted value checks out. If the person’s login name isn’t exposed, which is should NEVER be, it becomes a ‘key’ for the encryption, easily accessible to the MT program and the MT user, NOT to the spammer. And the different comment identifiers would make sure that the encrypted values changed with each comment.

Only problem with this solution is it would require cracking into the MT internal code.

Question: what do you think of this as a solution, and is it worth the time to do it?

(However, by now, Phil or someone else of like cailber will have found and coded a solution and have it half way distributed throughout the world. I should just leave these little challenges to others — what do I know?)

Categories
Technology Weblogging

Comment spammers redux

Recovered from the Wayback Machine.

Seems to be a technology day today.

Phil caught a comment spammer who was trying to dump spam comments in all of his posts. This process would work within any weblog that sequentially numbers weblog posts (ie Movable Type).

I’m going to try and tweak my mt-comments.cgi to stop POSTs from pages outside of my root URL. This is my way of warning you all that the comments, web pages, weblog may be a tad more behaviorally challenged than normal.

Update: I added checks on referers and this will prevent posts from locations other than my own weblog server. Unfortunately, as Phil pointed out, http referers are fairly easy to fake. I also wrote a test script that did so, and my checks failed to catch a ‘fake’ referer.

Still, it’s a start…

If you attempt to post a comment and fail, please send me an email and I’ll check to see what the problem is. Unless, of course, you’re the spammer. In which case: Eat dirt and die scuzzbucket!

Ahem. Thank you.

Categories
Technology

The Slinky style of project management

It seems that the bookshelves are inundated with books about building successful systems. “Build a successful business in 2 weeks or less”, or “How to go on the Web in 30 days”.

To me, the real key to building successful systems, Web-based or not, is to understand the underlying causes of what makes a system fail. So, I decided to write my own set of articles entitled “Why Systems Fail”, beginning with this — The Slinky Style of Project Management.

The What Style of Project Management?

You know what Slinkies are. They’re metal or plastic springs that can be pulled apart and spring back together. You can buy a slinky in its pure form, or as part of another toy. For instance, you might have seen them as the body of the Slinky Dog™ in Disney’s popular movies Toy Story and Toy Story 2.

I have a slinky at my desk, and whenever I get stumped on a technical problem, or I’m trying to figure out how to word a paragraph, I grab my slinky and move it back and forth between my hands. Back and forth, back and forth — really quite soothing. Okay, yeah, a bit mindless too, but that’s okay sometimes.

However, the fame of a slinky is its ability to “walk” downstairs. Put a slinky at the top of the stairs, tip the toy over the stair by its top and watch it go — the thing won’t stop; moving head over tail, overhead, over tail, until it hits some obstruction along the way, falls off the stairs, or hits the bottom.

The thing about a slinky is that the toy has momentum generated by the action of the spring and the force of gravity, and this momentum doesn’t cease until something blocks the action of the springs or the slinky is no longer falling.

Project Management can take on a strong resemblance to the slinky, particularly with system projects that are based on the Web. The real key characteristic of the Slinky type of project management is to continuously move, building on the previous momentum, and whatever you do, no matter what, don’t stop.

Doing design specifications and getting buy off from the clients means stopping — can’t do that. So is documenting the system as its being built, or using documentation in the code, or diagramming the application or the database supporting the application. All of these mean that you will be spending time Not Delivering Content — and not delivering content is evil, it means you’re stopping. So you just keep on putting content out, and putting more content out, and more and more. Just like the toy on the stairs, you hope that the momentum of your effort keeps the project going and going and going.

However, with this kind of project, when you do stop (and you will, eventually), just like the slinky your system is going to sit there most likely fallen over on its side. Folks are going to be able to look, really look, at what you’re delivering: applications quickly delivered but not very robust, that are hard to maintain and difficult to modify; they might have security leaks and the database data might be a bit suspect.

Some poor soul or group of souls will be brought in to support the system and they’ll get all the late night calls with system failures, and the code re-writes, and they’ll probably not like the originators of the system very much especially when they’re going through the one thousandth line of code trying to figure out where the bad data is coming from.

Normally, folks don’t use the phrase “Slinky style of project management” when they refer to this style of systems development. No, usually they use terms such as Rapid Application Development (RAD), and Content Focusing, stuff like that. But you can still see the faint gleam of metal of the slinky underneath.

RAD does not mean any documentation, nor does it mean no design: it means starting small and building on the successful completion of each task BUT (big but here), you still deliver specs, and you still deliver docs, and you still make sure appropriate T’s are crossed, and I’s are dotted. RAD is not a shortcut for doing things Right.

In the Slinky project management style, there are no system design documents, there are no system implementation documents, there isn’t an overall plan, there are no standards, source code control is a bit of a laugh, and there is no documentation of the database —

But hey, the ride was sure fun, wasn’t it? Just don’t stop, just don’t ever stop…

Categories
Technology

Demonstrating Photoshop Functionality with X-Objects

Copy found at Wayback Machine Archive.

I upgraded my original cross-browser DHTML objects into the new X-Objects — with support for Mozilla and Navigator 6.0.

To try out the new objects, I migrated 4 DHTML demos into using the new objects. These demos demonstrate different aspects of using Adobe PhotoShop’s photo filters and editors. I’ll be going over each demo in this article, but first, I wanted to describe another set of JavaScript objects used in the examples: the Animators.

Animator scripting objects

When I first started working with DHTML, and creating DHTML effects, I used to manually code animations within the page where the animation displayed. These animations used a JS timer created using the function setTimeout.

For instance, my first DHTML animation was about an animated menu that didn’t do much more than move pieces of the menu around the Web page (see animated menu). This example has changed based on new versions of both IE and Navigator, but for the most part the animation starts once the page is loaded, and continues until the last animation step is reached:

function MoveObjects() {
   theobjs["menu1"].objMoveAbsolute(
		TimingObjectsX[0][currentTick], 
                TimingObjectsY[0][currentTick])
   theobjs["menu2"].objMoveAbsolute(
		TimingObjectsX[1][currentTick], 
                TimingObjectsY[1][currentTick])
   theobjs["menu3"].objMoveAbsolute(
		TimingObjectsX[2][currentTick], 
                TimingObjectsY[2][currentTick])
   theobjs["menu4"].objMoveAbsolute(
		TimingObjectsX[3][currentTick], 
                TimingObjectsY[3][currentTick])
   theobjs["menu5"].objMoveAbsolute(
		TimingObjectsX[4][currentTick], 
                TimingObjectsY[4][currentTick])
   theobjs["menu6"].objMoveAbsolute(
		TimingObjectsX[5][currentTick], 
                TimingObjectsY[5][currentTick])


   currentTick++
   if (currentTick < 8) 
      setTimeout("MoveObjects()", 400)
}

I’ve also used setInterval as well as setTimeout to create repeating timers as well as non-repeating ones.

After a while, I started creating more complex animations such as DHTML demonstrations of how a hurricane forms, or the Dr. Dotty games, covered in a previous InterActZone article. One thing I found out is that the amount of code to control multiple objects in a synchronized animation was enormous and I got a bit tired of coding and re-coding the same timer synchronization efforts.

So, I created what I call the Animator objects. This set of objects provides for most major types of DHTML animation — movement, clipping, visibility, ordering, and sliding (combination of movement and clipping) — and has built in animation synchronization. So, you can have an animation sequence that moves 3 DIV blocks around the page, while a fourth block is being clipped and a fifth block is being hidden.

You can take a look at the Animator script file as I go through how the objects work.

How Animators work

There are three types of Animation objects used to build an animation.

First, an Animation Sequence is created. An Animation Sequence is a container for several different animator objects, all of which are animated as a unit. To create the Sequence object, use the following:

      seq = new animatorSequence();

Once the Animator Sequence is created, then Animators are added. An Animator adds a specific animation effect over a specified period of time, and over a specified number of steps. So, if I want an animation to last 100 microseconds, and occur over 15 steps, I would create the following Animator:

      var anim = seq.newAnimator(15,100);

I can add more than one Animator to a sequence, and each Animator is played in turn, as they are added to the sequence.

Once the Animator object is created, Animations can be added to the object. For instance, to move two different Web page DIV blocks at the same time, I would use Animator code similar to the following:

anim.addAnimator(theobjs["box1"],"M",160,320);
anim.addAnimator(theobjs["image2"],"M",-240,320);

In this, two DIV elements, named “box1” and “image2”, are moved at the same time, so they’re added to the same Animator. Each Animation is defined by the animation type — move type is defined with a letter “M” — and any characteristics of the animation, such as the end location of the move.

I could at this point add more Animator objects (there really is no limit to the number of Animators added to an Animation Sequence, other than limits within the underlying JS, such as array elements and number of timers). I could also add more Animations to the current Animator.

When I’m finished defining the existing Animation Sequence, I can then play it:

    seq.play();

What happens is that the animation starts, beginning with the first Animator added to the sequence, and continuing, in order, until all of the Animators have been played. Additionally, when an Animator is associated with more than one HTML element, the DHTML effect assigned is applied to each HTML element, in turn, in a synchronized manner over all of the steps of the animation.

The types of animations currently implemented are:

  • Clipping: addAnimator(theobj, “C”, newtop, newleft, newbottom, newright)
  • Movement: addAnimator(theobj, “M”, newtop_position, newleft_position)
  • Visibility: addAnimator(thobj, “H”) //Hide, use “S” for show
  • Display: addAnimator(theobj, “D”, display_type) // “block” or “none” for display
  • Z-Order: addAnimator(theobj, “Z”, z-order)
  • Replacement: addAnimator(theobj, “R”, replacement_string) // based on innerHTML
  • Parm_Replace: addAnimator(theobj, “P”, tag, classname, id, contents) // parameterized replacement
  • Slider: addAnimator(theobjs, “SL”, sliding_value, sliding_direction)

The Slider has a type of “SL”, but anything will do — it is the default animation. The first parameter is the type of slider (top to bottom, left to right, right to left, or bottom to top), and the direction specifies which way the slide will move. Note: The Slider has not been converted to working with Mozilla/Navigator 6.0 and may actually be deactivated from the Animators at some point.

Next up, we’ll use both the Animators and the X-Objects to create the PhotoShop demos.

Correcting a Photo

The first PhotoShop DHTML demo is based on using PhotoShop’s various tools and techniques to correct a bad photo. This demo is the simplest of the PhotoShop animations — using CSS absolute positioning and visibility, as you can see by accessing the Example page and picking the first demo.

To create the DHTML effects, the X-Objects and Animators are added to the page first:

<!-- cross-browser objects -->
<SCRIPT; src="cbobjects.js" language="javascript" 
type="text/javascript">>
</SCRIPT>

<!-- animation and sequence objects -->
<SCRIPT; src="animator.js" language="javascript" 
type="text/javascript">
</SCRIPT>

The content of the page is added, next. To ensure that the page’s DHTML effects work with Navigator 4.x as well as Mozilla/Navigator 6.0 and IE, all animated content is enclosed in CSS absolutely positioned DIV blocks:

<DIV; id="top" style="position:absolute; left: 20; top: 50; 
       width: 600;visibility:hidden">
Unfortunately, most photos do not turn out as we would like,
 or at least as much as I would like. Usually I'll re-take
 the photo, but sometimes this isn't feasible, or I like
 most aspects of the photo and hate to 
lose the shot. Enter Photoshop to the rescue. 
</DIV>

Once the content has been added, code is added to handle user actions. As the demo is opened into its own browser window, to close this window, the user can press the Space Bar. Additionally, hitting the ENTER key will cause the PhotoShop demo to proceed to the next “slide” in the DHMTL effect. As both actions are based on keyboard events, the keyboard events are captured and assigned to a event handler function:

   // capture events
   if (navigator.appName == "Microsoft Internet Explorer") {
	wdth = document.body.clientWidth;
	}
   else if (navigator.appName == "Netscape"){
	wdth = window.innerWidth;
   	document.captureEvents(Event.KEYPRESS);
      }
   else
      document.addEventListener("keyup",keypress,true);


   // assign function to event handler
  if (navigator.appName != "Mozilla")  
    document.onkeypress=keypress;

The event handler function pulls the keyCode from the event, and tests to see if the space bar or the ENTER key has been hit. If the space bar is hit, the page is closed. If the ENTER key is hit, the next slide is played if the demo is not at the last slide already :

function keypress(e) {
   if (!canbeginnow) return;

   if (navigator.appName == "Microsoft Internet Explorer") 
      tmp = window.event.keyCode;
   else if (navigator.appName == "Navigator")
	tmp = e.which;
   else if (navigator.appName == "Mozilla")
       tmp = e.keyCode;
   
   // if space bar hit
   if (tmp == 32) {
	window.close();
	return;
	}
   if (tmp == 13) {
      if (slide == MAX_SLIDES) 
	   return;
      next_slide();
      }
}

The code for the demon’s Animation Sequence is added next. There’s only one used for this PhotoShop demo — the animation played at the end of the demo. Its Sequence and Animators are created and played within one function:

// create animation sequence
function do_animation() { 

      // create new sequence
      seq = new animatorSequence();

      // add animator objects to sequence
      var anim = seq.newAnimator(20,100);

      // add animators
      anim.addAnimator(theobjs["image1"],"M", 200, 20, 0, 0);
      anim.addAnimator(theobjs["image2"],"M",-214,-300,0,0);
      anim.addAnimator(theobjs["image3"],"M",-214,500,0,0);
      anim.addAnimator(theobjs["image4"],"M",200,340,0,0);

      anim = seq.newAnimator(1,100);
      anim.addAnimator(theobjs["top"], "H", 0,0,0,0);
      anim.addAnimator(theobjs["top2"], "S", 0,0,0,0);
 
      anim = seq.newAnimator(20,100);
      anim.addAnimator(theobjs["image1"], "M", 150, 20, 0, 0);
      anim.addAnimator(theobjs["image4"], "M", 150,325,0,0);
     
      
      // play sequence
      seq.play();
}

There is only one Animation Sequence created, and contains 3 separate Animators. The first moves Animator is played over 100 ms, and consists of 20 steps. In this Animator, two of the demo page’s images are moved “off screen” (beyond page boundaries), and the remaining 2 are lined up next to each other. The second Animator consists of 1 step: hiding current text at top of demo page, and showing new text. The last Animator again is played out over 100 ms, and 20 steps, and this animation again moves the two images that are still showing on the page. After the Animation Sequence is created, and the three animators are added, the sequence is immediately played.

Finally, the code to handle each demo “slide” is added:

// play next presentation slide
// until last slide
function next_slide() {

   // return if animation is playing, or past last slide
   if (animatorPlaying) return;

   if (slide == 5) {
	alert("That's the end of the demonstration. 
               Re-load page to run again.");
	return;
      }

   theobjs["text" + slide].objHide();
   slide++;
   if (slide <= 4) {
    theobjs["image" + slide].objShow();
    theobjs["text" + slide].objShow();
    }
   else if (slide == 5) { 
    theobjs["text4"].objHide();
    do_animation();
    }	
}

Each “slide” consists of an image and text that are hidden together, or shown together. Notice that after the last slide is shown (slide 5) that the animation is then run.

Again, try out Demo1 from the the Example page. After clicking through all of the slides, you’ll trigger the animation. Note that it plays three separate animations, each after the other.

The next PhotoShop animation uses the tools layering ability to create a double image.

Creating a Double Image

The concept of “layers” existed in graphics, and particularly in PhotoShop long before they appeared in DHTML. An image can consist of more than one layer, with each layer having different content, color, what have you, but all of the layers combined forming the image.

A fun use of Adobe PhotoShop layers is to create a double image, and the second PhotoShop DHTML demo presents this, as you can see for yourself by accessing Demo 2 from the Example page.

 

Again, as with Demo1, the Animator and X-Object JS files are added to the page, and event handling is added. This doesn’t change from the previous example, so the code is ommitted.

In this new demo, there are several animation sequences, which are created individually as part of an array:

// setup animations
function animationSetup() { 

seq[0] = new animatorSequence(); 

var anim = seq[0].newAnimator(1,100);
anim.addAnimator(theobjs["top"],"H");
anim.addAnimator(theobjs["top1"],"S");
anim.addAnimator(theobjs["box1"],"M",160,wdth);
anim.addAnimator(theobjs["box1"],"S");

anim = seq[0].newAnimator(20,100);
anim.addAnimator(theobjs["box1"],"M",160,320);
anim.addAnimator(theobjs["image2"],"M",-240,320);
  
seq[1] = new animatorSequence();

var anim = seq[1].newAnimator(1,100);
anim.addAnimator(theobjs["top1"],"H");
anim.addAnimator(theobjs["top2"],"S");

anim = seq[1].newAnimator(5,110);
anim.addAnimator(theobjs["image1"],"M",160,145);

anim = seq[1].newAnimator(10,110);
anim.addAnimator(theobjs["image1"],"M",160,320);
anim.addAnimator(theobjs["image3"], "C",0,0,193,248);

anim = seq[1].newAnimator(1,100);
anim.addAnimator(theobjs["image1"], "H");
anim.addAnimator(theobjs["box1"], "H");

seq[2] = new animatorSequence();

anim = seq[2].newAnimator(1,100);
anim.addAnimator(theobjs["image2"],"M",160,-260);
anim.addAnimator(theobjs["image2"],"S");
anim.addAnimator(theobjs["top2"],"H");
anim.addAnimator(theobjs["top3"],"S");

anim = seq[2].newAnimator(20,100);
anim.addAnimator(theobjs["image2"], "M",160 ,60);

seq[3] = new animatorSequence();

var anim = seq[3].newAnimator(1,100);
anim.addAnimator(theobjs["top3"],"H");
anim.addAnimator(theobjs["top4"],"S");

anim = seq[3].newAnimator(5,100);
anim.addAnimator(theobjs["image3"],"M",160,310);

anim = seq[3].newAnimator(20,100);
anim.addAnimator(theobjs["image3"],"M",160,60);
anim.addAnimator(theobjs["image4"],"C",0,0,214,250);

anim = seq[3].newAnimator(1,100);
anim.addAnimator(theobjs["image3"], "H");
anim.addAnimator(theobjs["image1"], "H");

seq[4] = new animatorSequence();

var anim = seq[4].newAnimator(1,100);
anim.addAnimator(theobjs["top4"],"H");
anim.addAnimator(theobjs["top5"],"S");

anim = seq[4].newAnimator(10,100);
anim.addAnimator(theobjs["image4"],"M",110,180);
    
anim = seq[4].newAnimator(1, 100);
anim.addAnimator(theobjs["image2"], "H");
anim.addAnimator(theobjs["image3"], "H");
anim.addAnimator(theobjs["image4"],"H");
anim.addAnimator(theobjs["image5"],"S");
}

As you can see, there are 5 different animation sequences defined, for the 5 different slides of the DHTML demo. By using an array, only one sequence is played at a time.

This demo uses clipping. At this time, clipping as defined for IE 4.x and 5.x as well as Navigator 4.x, has a different frame of reference than that for Mozilla/Navigator 6.x. Because of this, in my current examples I test for the type of browser and use different clipping values based on the findings within the X-Objects (read more about clipping in X-Objects: Clipping).

Playing each slide of this demo actually plays each Animation Sequence as it is defined within the animation array:

// play next presentation slide
function next_slide() {
   if (animatorPlaying) return;
   if (slide > 4) {
	alert("That's the end of the demonstration. 
               Re-load page to run again.");
	return;
      }
   seq[slide].play();
   slide++;

}

Again, try out Demo2 from the the Example page.

Cropping a Photo

The third Adobe PhotoShop DHTML demo demonstrates how to use cropping within this tool. The DHTML used is primarily clipping with some movement, as well as hiding and showing content. I won’t repeat the code here, but you can see Demo3 yourself from the Example page. To see the demo code, choose View…Source from the browser menu.

Using the Rubberstamp tool to correct a background

The third Adobe PhotoShop DHTML demo demonstrates how to use cropping within this tool. The DHTML used is primarily clipping with some movement, as well as hiding and showing content. Again, I won’t repeat the code here, but you can see Demo4 yourself from the Example page. To see the demo code, choose View…Source from the browser menu.