Thunderclap, the Newsletter of Rolling Thunder
Computing
Volume 5, Number 3, Summer 2003
In this issue:
Blatant Self-Promotion: I'm a legend
in my own mind, New in-house class on Web Services, Learn .NET from me at
Harvard this fall.
Feature Article: Whither Web Services ? WS-Security and the Web Service Enhancements 2.0 toolkit
The Internet Chuckle: Not real funny, but check out the screen saver and spam blocker I use
Blatant Self Promotion: Would you buy a used car from this guy?
New Class on Web Services, and .NET at Harvard this Fall
Recognize me? Microsoft is running a new promotion featuring me and a few of their other top independent consultants, calling us "Software Legends." We're doing bookstore events and other promotions, such as events at Tech Ed and PDC. Part of the promotion involved a professional photo shoot in Redmond. I'm astounded that the skill of the photographer, Karen Moskowitz of Seattle (www.moskowitz.com), actually managed to make me look good. I've got posters available, suitable for framing or wrapping fish, and I'll mail you one if you promise to display it. Send me a photo of it on your wall (ideally without darts in it), and the best will win a prize. Here's what I'm up to:
NEW! 4-day In-House Training Class on Web Services
The main article in this newsletter describes Microsoft's new extensions to XML Web Services that actually make them useful for something besides saying, "Hello, World." They security pieces, in particular, are well thought out. Web Services are now sophisticated enough to warrant a topic in themselves, so I've written a 4-day class to do just that. It uses Microsoft's WSE version 2.0. See the syllabus online here, then call me to schedule yours.
5-day In-House Training Class on .NET or .NET for Insurance
.NET is here, and it's hot. It changes everything in the software business, and you can't afford to be without it. Rolling Thunder Computing now offers in-house training classes on .NET, using my book as the text. I've split it up into customized offerings, such as .NET for Existing 3-tier Microsoft Programmers, .NET Framework Serious Geekery, .NET Web Programming, and .NET for the Insurance Industry. See the syllabi online here, then call to schedule yours.
At Harvard Extension This Fall -- Hard-Core .NET Programming. Meets Mondays at 7:35 beginning Sept 15, Harvard Science Center Room 102B.
My class at Harvard Extension is coming up this fall. Last year I left my roots and taught a cream puff class, a few easy homeworks and a project the students chose themselves. While the evaluations were positive, I felt empty. The students hadn't gotten the best from me, from Harvard, or from themselves, and most of them didn't know it. But I did, and I do, and I know how to do it better, because I've done it. This year, I'm returning to my hard-core programming class, featuring a cumulative project, the approach I pioneered with my SDK class back in 1992. Students from those years still come up to me at conferences and tell me how that deep, persistent knowledge of how things really tick under the hood, not just clicking wizard buttons and saying, "Gee, whiz!", put them ahead of their competitors then and now. If you really want to get your hands dirty and don't mind working hard, this is the class for you. We'll cover the .NET Framework in depth, do a lot of Web Services, and even get into compact portable devices. See the syllabus online, then come on down the first night and check it out.
ACORD XML for Insurance In Depth
Insurance carriers, learn ACORD XML from David Platt. This 5-day class will give you everything you need to know to jump-start your application development. Learn about security and plug-and-play. See the syllabus online here. It can be customized to your needs.
Web services were originally designed in the euphoric days of the first Web platinum rush, with the NASDAQ index shooting through 3000 en route to 5000. Marketing types crazed on their own hot air turned cartwheels, shouting, "HOORAY! YOUR CELL PHONE WILL GET SPORTS SCORES AND STOCK UPDATES!!!" No one stopped to ask, "Hey, does anyone really CARE about that except as a short-lived curiosity for sexually frustrated geeks with nothing better to do on a lonely Saturday night?" And no one asked how anyone was going to make any money with it. Most people had some notion that web content should somehow be free, a hopelessly naive ideology sneered at today as "dot-communism". The open, free-for-all design of XML Web Services, with no security or encryption or distributed coordination, reflects that free-love philosophy. But having another computer program call yours is usually at least a semi-intimate transaction, in a way that's qualitatively quite different from having a human view your Web page. The giant cross-platform orgy never happened, despite sensuous lubrication with tons of Microsoft snake oil, and the NASDAQ is currently down around 1300.
Today we laugh and groan over our idiocy at the time, just as in our adulthood we wonder how we ever could have believed in Santa Claus and the Easter Bunny. The stock market continues its ballistic trajectory, which no one stopped to think has by definition a downward arc as well as an upward one. Security is the first thing that any application writer who wants to be successful needs to think about. Dot communism has imploded along with the political kind, as users find that the adjective "free" means, as it always does, "paid for by someone else", who insists on getting it back one way or another. As Charles Dickens wrote at the start of A Tale of Two Cities, "It was the best of times, it was the worst of times." And God, I love them. But that's just me.
That doesn’t mean that XML Web Services are useless, far from it. They represent the basic solution to a very important and vexing class of problem. But except for curiosity demo projects, my customers report that they are using XML Web services to connect heterogeneous systems within their own enterprises rather than outside them. For example, it’s common for an insurance company to have their Web servers on one type of system, their policy issue software on another type, their accounting and billing software on a third type, and need to tie them together. Meanwhile, the insurance company might acquire another insurer and need to integrate new systems with their own. XML Web services are excellent for crossing these types of platform boundaries. Connecting systems that they control over network wires that they also control is an order of magnitude easier than interoperating with random Internet passers-by, and the financial return much more certain. The shopping Web service that allows the Internet refrigerator to automatically order more orange juice is waiting for prefabricated solutions to more of the common infrastructural problems.
XML Web services are very much in their infancy today. As raw DCOM did on its release in 1996, they provide basic connectivity from one box to another. You shove bytes in one end and they come out the other. But as we discovered with raw DCOM, when you start making calls from one box to another, you encounter new classes of problems that which the basic connectivity package doesn't address, such as security, reliable message delivery, and operational integrity. These problems are common to most applications, so it only makes sense to address them at an operating system level rather than have each application vendor spending time and money to roll its own incompatible version. As Microsoft developed MTS and later COM+ to address these sorts of problems in the COM world, so they are now working on solutions to these problems in the Web services world. The complicating factor is that since Web services are fundamentally about cooperating with non-Microsoft systems, the design process requires discussion and compromises among many parties to reach agreement on how they ought to work.
For example, one of the first needs of any distributed system developer is user authentication, which generally involves presentation of some sort of credential, such as a user name and password. The base release of Web Services in .NET 1.0 and 1.1, demonstrated in the simple GetTime sample web service that I show in my book, doesn't have a standard for representing such things in the SOAP packet that carries the function call, as shown below:
<soap:Envelope>
<soap:Body>
<GetTime
xmlns="http://tempuri.org/">
<showSeconds>false</showSeconds>
</GetTime>
</soap:Body>
</soap:Envelope>
If you want to provide some sort of security implementation, you have to design the security architecture yourself, which you probably don't have the skills and experience to accomplish properly, without holes, and know that you've done so. Hell, I don't. For example, you might pass authentication credentials as parameters in a function call, sort of like this:
<soap:Envelope>
<soap:Body>
<GetTime
xmlns="http://tempuri.org/">
<showSeconds>false</showSeconds>
<userName>me@here.com</userName>
<passWord>false</passWord>
</GetTime>
</soap:Body>
</soap:Envelope>
I don't have time to discuss the security holes in this seemingly plausible architecture, but take my word for it, it has several. It's also inflexible, requiring users to authenticate in this way and no other -- no X509 certificates, say, or no tokens of trust from third parties. It's also brittle because your authentication information is mixed in with your business logic, so every time you change your authentication information you have to change every part of your business logic that touches it. It doesn't scale well either. You have to explain it to every client programmer, and they in turn have to learn every different buggy security architecture used by every misguided web service developer. And finally, to add injury to insult, you have to pay for developing, debugging, testing, documenting, and supporting this piece of crap. Web service authentication is a horizontal problem, by which I mean it's the same in any application problem domain (finance, medical, retail, etc.), and it needs to be solved at the horizontal operating system level.
So what has has to happen to get a horizontal solution? Basically, the user and vendor community needs to agree on exactly what the SOAP-based syntax, the XML problem domain vocabulary, if you will, should be for representing the information used for securing web services (and all the other things you want to do with them, such as transaction coordination). The SOAP spec provides the soap:Header element for exactly this purpose, holding infrastructural data, as distinct from the application data that goes in the soap:Body element. The SOAP packet for our GetTime call might look something like this (namespaces removed for clarity):
<?xml
version="1.0"
encoding="utf-8"
?>
<soap:Envelope>
<soap:Header>
<wsse:Security
soap:mustUnderstand="1"
>
<wsse:UsernameToken>
<wsse:Username>me@here.com</wsse:Username>
<wsse:Password Type="wsse:PasswordText">Fido</wsse:Password>
<wsse:Nonce>dIPLPzlgHrBMKOu04M5xeQ==</wsse:Nonce>
<wsu:Created>2003-01-25T17:36:31Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<GetTime
xmlns="http://tempuri.org/">
<showSeconds>false</showSeconds>
</GetTime>
</soap:Body>
</soap:Envelope>
You can see that the soap:Body contains the usual information for a Web service call, such as the name of the function and its parameters. In this simplest example, the soap:Header contains a Security element. The “mustUnderstand” element tells the receiver that the information in the Security element is so important that if the receiver doesn’t understand what to do with it, then the call has to fail. Inside the Security element is a UsernameToken element, which contains the user ID and password, along with the time of its creation and a nonce (a unique string identifying this particular login request so that no two of them are identical). The security information is separate from the business logic. The security design is significantly better, and potentially more flexible. And if we can just get people to agree on what the XML syntax ought to be, we'll have it.
So exactly who has to agree on the standard XML syntax? Microsoft on its own isn't big enough to establish a Web standard. They tried it with Passport and it didn't work. (Spare me the outraged e-mails from the Passport team. Your web site www.passport.com today lists 83 sites that use Passport as their basic authentication mechanism, up from 65 two and a half years ago, not exactly explosive growth, especially when you consider how much the Web itself has grown in that time. And 22 of those sites belong to Microsoft, plus Starbucks.com which might as well belong to Microsoft since they drink so much of it. You had to pull the plug on Express Purchase because not enough customers would trust you with a credit card to attract enough vendors. Passport is a convenient standard for single signon among the manifold parts of Microsoft, and as such fairly useful. As a Web standard, you swung and missed. Deal with it.) The whole point of XML Web Services is to interoperate between systems regardless of vendor. So we'll need a larger community than that.
The opposite end of the spectrum is to allow anyone in the world who is interested to participate. This leads to design by committee, which takes a very long time and comes up with a standard that flops around all over the map, not doing anything very well. The ancient Greek storyteller Aesop wrote a fable called "The Man With Two Sweethearts", which you can read online here. The moral is that "Those who seek to please everybody please nobody". So this problem isn't new. The latest example of this is the W3C XML Schema standard, about which the most charitable thing I can say is that everyone I know dislikes it more or less equally. It's clunky, it's not internally consistent, it's very hard to implement. The product of a committee must, by the Second Law of Thermodynamics, be dumber than the dumbest committee member. So inviting everyone in isn't the best answer either.
The current round of Web Service standards is being developed primarily by IBM and Microsoft together. You'll occasionally see a few other vendors invited to contribute specialist expertise, such as Verisign and RSA on the security parts and BEA on distributed transactions. But it's mostly IBM and Microsoft. It's a small enough team to move fast, so they've solved that problem The dumbest team member is five times smarter than me, at least on this kind of project, so they've solved that problem too. The only question is whether these two companies together carry enough clout to swing the market their way, and I think they do.
Obviously not everyone is pleased at being left out. IBM is carrying the standard for the Java world in this design rather than Sun, who own Java are reported to be absolutely furious. But Sun's stock price is $3.82 as I write these words. A share of it might get you a cup of plain coffee, but it won't get you a Starbucks triple latte with butterscotch sprinkles. Sun is pointless. Sun is toast. Sell your Sun stock. (on the other hand, at this point, why bother?) They're dead, they just haven't stopped breathing yet. You heard it here first. (Of course, dear readers, that could be said of all of us.) Oracle is also said to be miffed. But opposition seems to be scattered, and I don't see any other credible standard on the horizon for this kind of thing. EbXML is trying to do something similar, but they suffer from the committee design problems I just discussed, so I don't see them as a threat.
Together, the consortium has written a number of specifications all beginning with the characters "WS-", as in "Web Services". You'll find a list of them online here, with links to the current documents. The following table summarizes them:
WS-Addressing |
Transport-neutral mechanisms to address Web services and messages. |
WS-Coordination |
Protocols that coordinate the actions of distributed applications. |
WS-Inspection | An XML format for assisting in the inspection of a site for available services |
WS-Policy |
Describe XML Web Service requirements, preferences, and capabilities |
WS-Referral |
Allows dynamic configuration of WS-Routing paths |
WS-ReliableMessaging | Allows messages to be delivered reliably between distributed applications in the presence of software component, system, or network failures. |
WS-Routing |
Define paths for routing SOAP messages over a variety of transports |
WS-Security |
Defines security for all XML Web services. Contains several sub-specifications. |
WS-Transaction | Defines coordination types that are used with the extensible coordination framework described in the WS-Coordination specification |
Fortunately, you probably don't have to read these specifications. They're written in computer legalese, which is necessary for specificity but absolute hell for trying to stay awake. They're intended for plumbers, the programmers who create the tools that the rest of us use for putting these services in our applications. Unless this describes you, you probably don't need to crack them at all.
Specifications without some sort of implementation are useless piles of paper. If you had to write your own implementation of this standard, you'd be programming for years on infrastructure and never get any useful application work done. Microsoft released the Web Service Enhancement kit version 1.0 in December of 2002, containing early prefabricated support for WS-Security and WS-Routing. They've just released the beta version of 2.0, which you can download from their web site at http://msdn.microsoft.com/webservices/building/wse/. It updates and enhances version 1.0, as it should.
I've written a small sample program in WSE 2.0 to demonstrate some of the features of WS-Security, the authentication piece that I discussed previously. You can click here to download the sample code. Here's a screen shot of it.
Suppose I want my GetTime Web service (aren’t you sick of that one by now? I sure am. Remind me to think of something else for my next book. Any ideas?) to require user authentication so I could see if the client had paid his subscription fees before delivering the valuable service. I’d like to implement authentication with a user ID and a password according to the WS-Security standard. This means that the client has to somehow place the XML elements shown previously into its outgoing SOAP packet, and the server has to somehow read them and use them. Because the Web Service infrastructure has been so successfully hidden from application programmers, it’s not immediately apparent where the code to do this should go.
The Web service infrastructure employs a set of SOAP filters during its input and output operations, as shown here. A SOAP filter is a piece of code that gets invoked by the Web service during the process of sending and receiving a SOAP packet for the purpose of modifying that packet. The process looks like this:
While an in-depth discussion of SOAP filters is beyond the scope of this article, they’ve existed since version 1 of Web services, and I even wrote a newsletter article in 2001(click here to see it) about how to write your own. The Web Service Enhancements are implemented primarily as a series of SOAP filters. This diagram, extracted from the documentation, shows the filter sequence from the standpoint of the server:
Here's a picture showing shows a close-up of the modification process. Note the SOAP Context object which contains information about the filter application process. It represents the connection not only from one filter to another, but from the entire filter chain to the application, on both client and server. We'll make use of that context in our application code, as you'll see.
Here's a listing of the client-side code. To write it, I first had to install the WSE SDK, and import references to Microsoft.Web.Services into my project. Then I generated a proxy for my Web service by importing the WSDL file of the service as usual. Once I generated the proxy, I had to change its base class to Microsoft.Web.Services.WebServicesClientProtocol, the WSE class that understands the new client-side XML features. You’ll have to repeat this change every time you regenerate the proxy.
The first line of code is the same as for any web service, creating the proxy object. Next, I create an object of the new WSE class UsernameToken. This object exists to hold a user's name and password. In its constructor I pass the user name and password that the user has entered. Next, I fetch the SOAP context via the proxy class property RequestSoapContext, and place the token into the context’s collection of security tokens.
When I make the web service method call in the last line of code, the proxy (as always) constructs the SOAP packet for making the call. In this case, however, the WSE proxy has added an output filter that converts the security token into the XML you saw previously (in addition to some other items, such as a timestamp, which I don’t want to discuss here).
This sample also demonstrates the use of a digital signature block to protect against tampering, if the user checks the provided box. This tells the sender to run a hash algorithm on the output SOAP packet, and send the computed hash as part of it. The recipient will recompute the hash on the packet it receives and compare it to the hash that was sent. If they match, then the packet hasn't been tampered with and the call is allowed to proceed. Otherwise, the sever-side SOAP filters throw back an exception. The sample program provides a way of tampering with the packets (using a custom SOAP filter that I won't discuss here) to test this.
private void button1_Click(object sender, System.EventArgs e){
// Create proxy object (same as non-WSE)
localhost.Service1 proxy =
new localhost.Service1 () ;
// Fetch the SOAP Context object for the request
SoapContext soapContext = proxy.RequestSoapContext ;
// Create a new UsernameToken with the user ID and password
UsernameToken usernameToken =
new UsernameToken (textBox1.Text,
// user nametextBox2.Text,
// passwordPasswordOption.SendPlainText) ;
// Place UsernameToken into SoapContext for
// sending to the server
soapContext.Security.Tokens.Add (usernameToken) ;
// If the user has specified this choice, then add
// a signature block
if (checkBox2.Checked == true)
{
Signature sig =
new Signature (usernameToken) ;soapContext.Security.Elements.Add (sig) ;
}
// And if the user has told us to tamper with the output // as a demonstration, then add the filter that would do that if (checkBox3.Checked == true)
{
MyOwnTamperer t =
new MyOwnTamperer ( ) ;proxy.Pipeline.OutputFilters.Insert (0, t) ;
}
// Call web service via proxy (same as non-WSE) try
{
label1.Text = proxy.GetTime (checkBox1.Checked) ;
}
catch (Exception x){
MessageBox.Show (x.Message) ;
}
}
The process on the server side is conceptually similar, but even easier. The Web service itself is identical to the previous ones that got the time, so I won’t bore you with it. To get the SOAP filters in the input chain, I make entries in the web.config file. I won't take the space to show them here, but you'll find them in the sample code. Once I do that, the WSE takes over automatically and gives me reasonable default behavior. When a packet containing a UsernameToken comes in, the SOAP filters makes a call to system API function LogonUser( ), passing the user ID and password sent by the client. If this call succeeds, then the filter chain process is allowed to proceed, calling the web service's target method if the whole thing completes successfully. If LogonUser( ) fails, the SOAP filter throws an exception back to the client. If a signature is present, the SOAP filter checks it for correctness, throwing an exception if it isn't. Your web service method gets called if the underlying infrastructure is satisfied, and not if it isn't. Simple behavior is very simple to obtain, which is the hallmark of a good design.
I've put in a couple of tweaks just to demonstrate the sorts of things that are possible. It's good security design to disable a user's account after a few unsuccessful login attempts so as to stop a password guessing attack before it can work. If you'd like to be notified of this, you can derive a class from the WSE base class UsernameTokenManager and override the method OnLogonUserFailed. This gets called, as you could probably guess from its name, when the call to LogonUser fails. You put your own code here to count the number of bad attempts and lock out the account when there are too many of them. In this case, I simply output a trace so you can see it getting hit. If you didn't want to use LogonUser, which means every user has to have an account on your Windows system, you could override the method AuthenticateToken and plug in your own authentication scheme instead (not shown). You tell the SOAP filter chain to use your class rather than its own token manager by making entries in the web.config file (not shown either, but see the sample).
public class MyOwnUsernameTokenManager : UsernameTokenManager{
protected override void OnLogonUserFailed(UsernameToken token)
{
// Log the error. In production code, would probably lock a user
// account after a certain number of bad attempts
System.Diagnostics.Trace.WriteLine ("Logon failed for user '" +
token.Username + "'") ;
// Delegate call to the base class. This is what actually throws the
// exception
base.OnLogonUserFailed (token);
}
}
So far we haven't let our business logic touch the WSE at all, but suppose we want or need to. Perhaps our business logic needs to know who the authenticated user is so that we can respond to him by name, or look him up in the database or something. That information is in the SOAP context, from which my Web service needs to pull it. The static property SoapContext.RequestSoapContext.Current fetches the current SOAP context object, which contains that information. The only slightly annoying thing is that I need to loop through its collection of security tokens to find the one that I want, it isn't just there as I'd like it to be.
[WebMethod] public string GetTime (bool ShowSeconds)
{
// Get time to return
string time ;
if (ShowSeconds == true)
{
time = DateTime.Now.ToLongTimeString () ;
}
else
{
time = DateTime.Now.ToShortTimeString() ;
}
// Fetch authenticated name from the UsernameToken
UsernameToken usernameToken = MyOwnGetFirstUsernameToken (
RequestSoapContext.Current) ;
// Prepend authenticated user name
return "Hi, " + usernameToken.Principal.Identity.Name +
", it's now " + time + ". Do you know what time it is?" ;
}
private UsernameToken MyOwnGetFirstUsernameToken (SoapContext requestContext)
{
foreach (SecurityToken tok in requestContext.Security.Tokens)
{
if (tok is UsernameToken)
{
return (UsernameToken) tok ;
}
}
return null ;
}
This simple example showed the basic mechanism that will provide enterprise-level features to Web services, and the current implementation of the Microsoft software for providing it. I need to make a few notes here: First, I stripped the example to its barest bones so you can see how it works with a minimum of distraction. I omitted a number of other security features that a real life application needs. For example, I transmitted the password in clear text, which you probably shouldn’t do. The WSE contains support for several types of encryption, and you probably ought to use one. But this example should crack the problem open for you and make it easier to understand the implementation of these other features.
I now have a 4-day training class on Web Services alone, without any other features of .NET. It's available in-house at your company. Give me a call if you want it, or another on .NET.
We'll continue our discussion of .NET and Web Services in the next issue. Give me a call if I can help you out. Until next time, as Red Green would say, "Keep your stick on the ice."
New Edition: Introducing Microsoft .NET, THIRD Edition
Updated to V1.1 Release, with New Chapters Added on Remoting and Reflection, and Now Including "Tips from the Trenches"
Sample code in both VB and C#
by David S. Platt, President of Rolling Thunder Computing
Published by Microsoft Press
ISBN 0-7356-1918-2
Microsoft is now shipping .NET version 1.1, so I decided to update my book on it, and add lots of new material that I didn't have time to cover before, including chapters on .NET Remoting and .NET Reflection. I've brought all the material up to date with the 1.1 version of the product. I've added enhanced material so several of the previous chapters, a sample of which forms this newsletter's feature article. And I've added 2 new chapters, as you can see in the table of contents below. And as many readers asked for, I've written all the sample code in both VB and C#. If you liked the first or second editions, you'll probably like the third one as well. I tried really hard to make it worth making your boss pay for again.
I had to remove the chapters on Windows Forms controls and Web Forms controls in order to hit the target page count, with the addition of the reflection and remoting chapters. However, I've made those two chapters available free online at the book's web site, which is , naturally, www.introducingmicrosoft.net.
If you haven't seen it before, this book is meant to be accessible to managers, while still providing enough information to be useful to programmers, both lightweight VB types and heavyweight C++ types. The book is meant to be non-threatening, using a lot of pictures and diagrams, a small amount of simple VB code, and no C++ at all, which would scare off 80% of the potential readers. I write each chapter in a pyramidal structure, so managers can read the first three sections (Introduction, Problem Background, Solution Architecture). Ambitious managers and VB programmers can continue through the next section (Simplest Example). Heavier-duty developers can read the ends of the chapters, where I discuss more advanced elements of the topic. You'll also find a glowing review of the first edition here (well, what other kind did you expect me to link to? I may be crazy, but I'm not stupid.) written by Manohar Kamath. Here's the table of contents:
1. Introduction |
7. XML Serialization and Support |
2. .NET Objects |
8. Events and Delegates |
3. ASP.NET |
9. Threading |
4. .NET XML Web Services | 10. .NET Remoting (new chapter) |
5. Windows Forms | 11. .NET Reflection (new chapter) |
6. ADO.NET | 12. Epilogue and Benediction (hasn't changed much) |
The third edition should be on shelves by the time you read this. You can order it through amazon.com by following this link . It should be popular. The two editions were, and this one's better. On June 11, 2001, the first edition's sales rank on amazon.com was #762, thumping Tom Clancy's latest hardcover and paperback, hopelessly mired around #1400. That tells you something about Amazon's customers -- they're all geeks.
Much as I hate to say it, I haven't seen anything on the Internet lately that's met my standards for humor -- a deep belly laugh from a cause that others would think somewhat sick. For example, my favorite good new / bad news joke is: "The bad news is that we amputated the wrong leg. The good news is that your other leg is getting better after all." So instead, I'll give you my two favorite utilities, my screen saver and SPAM blocker.
The screen saver is an absolutely beautiful tropical salt water aquarium from www.serenescreen.com. The fish swim in a lifelike way, the starfish crawls up the glass, the air bubbles away. There are 25 fish that you can choose from or allow the program to randomly select. Free trial download, $20 for the production version. There's aversion that displays clock and calendar, which I don't use. You can get it customized for your company for a few hundred bucks. There's even a site for fans of it, online here. Here's a screen shot of it, but you need to see the full motion to appreciate it.
I don't hate spam for itself. But I get 500 of them a day, and spending even a second deleting each one is a bigger chunk out of my day than I can afford to spend on such bullshit. I've tried a number of content-based blockers, but they all had unacceptably high false-positive rates. A prospective client once couldn't get through to me for a week because the spam blocker that I used at the time kept bouncing her mail back, and I still haven't figured out why. (That client finally managed to get through, and I actually did some work for her, but it took me over 3 months to get paid. So I guess we're even now, right Donna?)
The problem is that it's very hard to define spam content in a rigorous mathematical way that computer programs can apply. However, a little human judgment solves the problem very easily. As US Supreme Court Justice Potter Stewart wrote about obscenity in Jacobellis v. Ohio (878 U.S. 184 (1964)), "I know it when I see it." You know spam when you see it, we all do. If we could somehow bottle and use that knowledge, we'd have an unbreakable spam filter.
That's what Cloudmark (www.cloudmark.com) has done with their SpamNet product, which plugs into MS Outlook. When you get an e-mail, SpamNet connects to their central server and transmits a fingerprint of the message, checking to see if it's been reported as spam. If so, the message gets shunted to your spam folder and optionally marked as read.. Invariably, a few spam messages get through to your inbox. You select them and click the "Block" button, which not only moves the message to your spam folder, but also reports it to the central server as spam for when the next guy comes looking for it. It's a distillery for the user experience. I've found that it traps over 90% of Spam, higher when I've been away for a few days and download a huge backlog that's been sitting there for a few days. In this case, I'll find that it knocks out about 480 of 500 messages, leaving 4 real messages and 16 undetected Spams. That's about a 96% correct trap rate. Most important, I've never ever had a false positive (that I know about, of course). It occasionally happens that e-mail newsletters such as this one are reported as spam by other users, you can explicitly whitelist these if it causes trouble for you. The cost is currently $4 / month, which I find reasonable. As with most downloadable software, there's a 30-day free trial.
Disclaimer: This section will often refer to commercial vendors of products or services. Rolling Thunder Computing receives no compensation from these vendors in any way, shape, form, or manner; and would decline any if offered.
And now, the moment for which I know you've all been waiting -- the pictures of my two girls. Lucy Katrina is now six and a half months. She's just beginning to crawl, and eat rice cereal. She loves her big sister, lighting up whenever Annabelle comes near, especially when she watches Annabelle bouncing on her bed. Lucy's already a seasoned traveler, accompanying her Dad on trips to New Hampshire, Indianapolis, Atlanta, New York, Orlando, and Stockholm. Seattle and Iceland are on the schedule.
And here's Annabelle, the first girl, now three years old. She's very protective of her little sister, telling strangers on the playground to stay away from her, "because she's OUR baby." She loves books and animals, especially octopuses. She got to go behind the scenes at the New England Aquarium and meet the one that lives there. She's looking forward to seeing the new octopus exhibit at the Seattle Aquarium, the first aquarium ever to raise a Giant Pacific Octopus from an egg. She says, "When I'm a grownup lady, I'll have my own octopus show like the sea lion show [that we see regularly at the New England Aquarium]. But I have to think what would be the best way."
Thunderclap is free, and is distributed via e-mail only. We never rent, sell or give away our mailing list, though occasionally we use it for our own promotions. To subscribe or unsubscribe, jump to the Rolling Thunder Web site and fill in the subscription form.
Legal Notices
Thunderclap does not accept advertising; nor do we sell, rent, or give away our subscriber list. We will make every effort to keep the names of subscribers private; however, if served with a court order, we will sing like a whole flock of canaries. If this bothers you, don't subscribe.
Source code and binaries supplied via this newsletter are provided "as-is", with no warranty of functionality, reliability or suitability for any purpose.
This newsletter is Copyright © 2001 by Rolling Thunder Computing, Inc., Ipswich MA. It may be freely redistributed provided that it is redistributed in its entirety, and that absolutely no changes are made in any way, including the removal of these legal notices.
Thunderclap is a registered trademark ® of Rolling Thunder Computing, Inc., Ipswich MA. All other trademarks are owned by their respective companies.