Saturday, September 27, 2014

The Encrypted File Storage System (EFSS) saved my butt

I've been testing a beta product called the Encrypted File Storage System (or EFSS) in a production environment for a while as an offsite backup solution. I'm only backing up 200MB of compressed data, but it works very well. Most backup solutions rely on pushing or pulling the data across a network. EFSS puts data to be backed up into an emulated file system locally and transparently encrypts and compresses the data. Each 4KB block of data has a timestamp associated with it, which makes incrementals over a network blazing fast - faster than anything else I've used.

Yesterday I was monkeying around on my server and removed a few installed packages that I didn't need any more. Unfortunately, removing those packages caused a critical configuration file to become corrupted. I then fired up the EFSS command-line shell and mounted everything except the last 24 hours of incrementals and exported the configuration out to the file system. I did an eyeball diff, checked a few things on the file system, and restored the configuration back to what it had been. If I didn't have an EFSS-based backup (e.g. had been using rsync), I would have had to rebuild the configuration from scratch and that could have taken several days instead of a few minutes.

You know your backup solution is working when you can rapidly recover from a data loss. EFSS also recently helped me to move a functional website from one server to another with permissions, owners, groups, and timestamps completely intact in a fraction of the time it would have taken me using other backup systems.

Thursday, July 31, 2014

An adventure in writing a PECL extension for PHP

Okay this isn't so much a guide on how to write a PECL extension as it is to discuss my recent experience in writing a PHP extension, publishing it, and documenting it. My hope is that by reading through the struggles I went through, that others can benefit.

Writing an extension for PHP requires serious skills and patience. Extension writing is a complex macro dance and really requires good planning to pull off successfully. In my case, I wanted to introduce native named synchronization objects into PHP. This is something I've felt has been missing from the language for far too long and I didn't see anyone else doing work in the area. The first thing I did was write a cross-platform library in C++:

Cross-platform C++ library

Writing a C/C++ library as a proof-of-concept is a good start to writing a PHP extension. I highly recommend it. Developing a separate library allowed me to work out most of the kinks in the logic apart from the Zend engine. After I solidified the working model, I began work on the extension itself. I had to port the library from C++ to C, but that was a fairly trivial operation.

The other major thing I did in advance of writing the extension was occasionally dig deep into the PHP source tree to figure out how functions actually worked behind the scenes. Some functions are so hopelessly complex (e.g. file handling because of URL wrapper support) that they are simply too dense to understand. Other functions, however, make for great snippets to commit to memory. The prerequisite time with the PHP source code before writing an extension is, IMO, about 3 to 4 months of casual interaction. That is just long enough to feel comfortable navigating the PHP source tree. If there was one thing I learned here is that every function in PHP is part of an extension, which may come as a surprise to many people.

The frustrating thing about PHP extension writing is that there is almost no documentation on how to go about doing it. There's a book (dead tree edition) by Sara Golemon on the topic and little else beyond a few minor, slightly dated blog posts. I ended up doing what most extension writers do - scouring the source code of other extensions to cannibalize specific ideas to write my own. "Simple" things in normal C such as returning a value become a Zend macro in PHP. Knowing which macro to use and what all the crazy options do is the hard part. Since I was making a set of object-oriented classes in PHP instead of functions, the amount of documentation on the topic approaches zero very quickly. So scouring other code based on the public documentation on helps to figure out which macro is probably the right one. Having a good understanding of the source tree structure helped go a long way to figuring things out on my own. Ultimately, extension writing for PHP is a fairly dark art, which may explain why there aren't a ton of extensions out there. I got the distinct impression that the developers like it that way to require a minimum level of software development competence before work on an extension may begin.

That said, the PHP documentation on extension writing does a decent job of getting developers started. The 'ext_skel' script makes a mostly working skeleton for a new extension. Not bad. I think the main issue I ran into regularly was that ./buildconf has to be run with the --force option until the configuration file is finalized when using the release builds of the PHP source tree.

I highly recommend developing an extension on Linux first and then porting it to Windows after that. The compiling environment on Linux for PHP is far superior to the Windows build environment. However, if you are like me and prefer Windows text editors and IDEs (i.e. don't like Linux editing tools), do what I did which was to use WinSCP to act as the go-between and then I used my favorite Windows-based text editor to edit the source code. Doing that worked out pretty well for me. And since I had worked out all of the core issues with my extension in a separate library (which allowed me to fire up Visual Studio for real debugging), nearly all of the extension writing process was porting the code (easy) and writing plumbing to connect it to Zend (more difficult).

I also developed a small test suite to validate that the code was working as expected. I highly recommend making a small test suite as it helps catch bugs in the code.

Once the Linux version was done, I went and tested it on Windows. The Windows build system, as mentioned earlier, is more fragile. One wrong character in the wrong place and the whole Configure.js script will blow up without specifying why. Setting up the Windows build environment is also a bit more difficult as there are several distinct pieces that have to be in the right place. However, once everything was in place, it built just fine. Again, a small test suite can come in very handy for tracking down bugs.

At this point, the extension was developed but my adventure was only beginning. See, I wanted it to be a PECL extension. If I were simply satisfied with just having a PHP extension that anyone could compile into PHP, that would be the end of it. However, PECL sprinkles on some special magic that transforms an extension into something that people want to use because an extension is suddenly easy to install via "pecl install extensionname" and then PECL handles downloading, extracting, compiling, and installing the extension. Also, package maintainers for major OSes like Ubuntu will pick up the extension and make it easy to install with package management tools like 'apt-get'. There are huge visibility advantages to deploying an extension via PECL.

Releasing on PECL requires approval. The PHP devs are rather stringent about who they let in, so I knew I needed to make the new extension relatively awesome. I got on IRC and the PECL dev mailing list and started several discussions. This is a very important step as there are conformity issues that will crop up. It is important to be super-flexible and willing to make changes to the code. I ended up moving a GitHub repo around and revamping a lot of code during this process. Once everyone seemed to be cool with the work that had been done, it was time to apply for access to PECL, PHP, and documentation repos via the PECL signup form. It's a completely laid-back process - therefore, after applying, I recommend just finding another project to work on. It can take up to two months for the devs to get around to accepting new users and getting those users set up with the appropriate level of access.

The hardest part about developing a PECL package is figuring out what the "correct" way to develop an extension is. If writing a regular extension is somewhat of an obscure task, the process of releasing a PECL package is more so. This isn't really anyone's fault since there isn't a whole lot of need for new general-purpose extensions for the language to begin with. So hammering out a good guide is a bit of a low-priority when there are other, more pressing matters to attend to. Plus, I'd wager that it raises the bar to entry somewhat significantly. PHP is software used on millions of hosts, so it needs to have some semblance of quality control applied to it. Obscuring the process of writing and releasing an extension is a pretty good solution to that problem.

At any rate, once the PECL account is approved, a whirlwind of activity happens. In general, I had already generated a PECL package but I had to regenerate it after rewriting parts of my 'package.xml' file. I had also looked at what doing documentation would require. But by the time the approval actually happened, I had kind of forgotten what I had done, so I made sure the test suite still passed and the extension still built as a sanity check. In all, it only took about one weekend to do the actual release and documentation cycle. Again, looking at how other extensions do things helps a lot with creating a consistent experience.

Here's the PECL package:

Here's the documentation:

The original version 1.0.0 of the extension had a bug that only showed up on some hosts that the PHP dev team caught. I tracked it down and fixed it and sheepishly released the 1.0.1 version of the extension. Interestingly, the 1.0.1 version received an "automatic" Windows DLL. I didn't build it but I suspect the team was waiting for a fix for the bug before letting the system do the Windows build. Also, within five minutes of 1.0.1 being released there were 40 downloads according to the PECL stats page. I assume there are automated processes sitting on the announcements list looking for new package uploads to PECL - either that or crazy people.

The documentation writing bit was a different experience too. Good documentation includes code examples that cover real-world scenarios (i.e. not contrived). The PHP documentation is written in a giant set of XML files. The primary way to introduce documentation is via Subversion. The main way to adjust the documentation is via a custom web-based GUI that the team has come up with. At the time of this writing, there is a site that publishes the latest documentation every six hours and the main PHP website and mirrors are updated every Friday. The GUI is pretty neat in that it attempts to manage the translation teams and tracks which bits of documentation are no longer building (because XML is pretty fragile). Because I was introducing new documentation into the tree, it was far more efficient for me to use the Subversion route. I made sure that my changes built locally without issues before committing them back into the main repository. Because so many people are involved in PHP development, it is very important to tread carefully when committing anything (code or documentation) and try to avoid breakages.

In my opinion, the source code is simple enough to use as a tutorial extension that does something useful without being overly complicated for those interested in writing object-oriented extensions for PHP. I would study the C++ library first to understand that code. Then the similarities between it and the PECL package stick out and it becomes easier to understand the more obscure Zend bits.

Hopefully these tips help someone out with their extension writing efforts.

Thursday, May 29, 2014

FreedomPop "free" plan is a bit dishonest

I've recently been exploring the world of FreedomPop on behalf of a friend who is going through a really rough patch in his life. FreedomPop sells WiFi hotspot devices that supposedly get 500MB of data per month for free. The only thing to pay for is the device itself, which will set someone back about $50. It sounds awesome, but that's really all it is.

The old adage, "If it sounds too good to be true, it probably is" definitely applies here. This plan is marketed as 500MB of data per month for free "forever" (the 'forever' is implied). Even 3 years of use (i.e. until the lithium ion battery wears out) would be enough to help my friend out in a significant way. I was willing to play guinea pig for this interesting service because I also have some potential use for it.

So, I went and bought the device, which set me back a little over $50 (after tax). Then I waited. And waited. And waited. And waited some more. And pretty much forgot about it until it randomly showed up about a month and a half later. In addition, these devices are "refurbished", but who really cares about that as long as they work? At any rate, the extensive waiting is the first warning sign that something might be fishy here.

The device that arrives is a "Sprint (now Netgear) Overdrive Pro (3G/4G)" hotspot. The free plan claims to run only on 4G (you have to pay to get 3G), which is technically accurate. What FreedomPop fails to mention up front is that it only runs on 4G WiMAX and that the device has no support for 4G LTE. So you can be bathed in 4G LTE service all day long but the device will never connect to it. It's a tad misleading as users think they will be connecting to 4G regardless of the type of 4G service. Unless a user is intimately familiar with all of the various forms of 4G out there, it is unreasonable to expect them to understand the difference between 4G WiMAX, 4G LTE, and other 4G variants.

Additionally, the device has to be manually configured before it will function properly. This may be beyond the skill set of some users. The 3G PRL and 3G profile have to be updated via the admin before it will connect to 3G. Again, 3G isn't free but it will connect once the PRL and profile are updated. The first time I tried this, the device had fits and I had to perform a soft reset (hold the reset button for six seconds while it is powered on) and try again before it succeeded the second time. Also, firmware updates have to be applied via the admin before 4G WiMAX will function at optimal settings. That last part is tricky because it looks like a large batch of refurbished devices, including mine, were modified in a way that prevents updates from being applied to the device. It appears that someone intentionally changed the SKU of each device from SKU 1453010 to SKU 1453012. The device firmware checks the SKU and checksums of a new firmware before applying it. So multiple users are getting the message "The update cannot proceed. There is a SKU version mismatch." when they upload the latest firmware.

(It may(!) be possible to alter the SKU of the device via a configuration file import, but the importer appears to verify a checksum, so that creates a new problem since the configuration file can't simply be edited with a text editor. The "simple" solution to that problem is to find someone with a SKU 1453010 device and import their configuration, which should correct the problem and allow the firmware update to proceed. I am still working on this approach, so don't do anything here. I'm willing to brick my device at this point.)

Also, Sprint is terminating WiMAX service in 2015. Anyone on the free plan currently able to get 4G WiMAX service will suddenly have a paperweight unless they sign up and pay for 3G service. 3G still enjoys a wider adoption rate, but, as anyone who has used 3G knows, it is rather sluggish.

Basically this reads as:

Warehouse operator: "Oh man, we've got all these devices sitting around our warehouse and Sprint is going to make them basically useless in under a year. We need to move this inventory out right now."

Marketing director: "I know! We can just give users WiMAX for free but not tell them about it until they've received the device and try to use it. We'll just advertise it as a 'free service with 4G only' because people will love the idea of 'free 4G'. We'll get rid of the devices and make some money."

It's definitely a brilliant strategy for moving inventory that no one will want soon enough. In addition, the way it is being marketed also phrases it such that people can be misled to believing that they will also get 3G service for free, which they won't. It is a bit dishonest to do that to people. In particular, this plan is being advertised to people who are classified as "low income" as being a way to get free Internet access "everywhere" they go (the 'everywhere' is implied). If 4G WiMAX is readily available in the area, it might be a viable temporary solution for someone who has no Internet access. It is also potentially useful as a device for setting up a quick WiFi LAN between two WiFi enabled devices vs. messing around with ad-hoc networks. So it isn't really a scam, but the way it is marketed isn't completely honest either - being especially unfair to low income individuals and families who can't afford a $50 loss.

If it had worked out (i.e. 4G LTE capable), this plan would be a game changer in the industry. It would force every telecom to finally lower their rates to sane levels. If you pay more than $10/month for unlimited text, talk, and data, then you are being ripped off and are paying too much for service.

Friday, May 09, 2014

Is Firefox 29.0 "ugly"? Then try this...

I run Windows 7 Ultimate, full Aero effects, and Firefox. The most recent update of Firefox to version 29.0 resulted in yet another redesign of the tabs. This redesign is very unfortunate because it makes the text completely unreadable. I have some very choice words for the Firefox developers about their general competence, but that's not what this post is about.

This post is for the average user that this garbage release was foisted upon. I've been running the fix for about a week now and, while not perfect, it is much better than not being able to read the text on my tabs at all.

Go here:

Click "Add to Firefox". Done.

That's the best theme I could find that balances readability, usability, and some semblance of elegance.

Saturday, May 03, 2014

Reinventing the office chair

My current office chair I use at home that I bought a decade ago for about $100 (it was on sale) is starting to fall apart. One would hope that innovation over a decade would result in improvements.

First off, the office chair you sit in probably looks like this:

If you are lucky to work for a really nice employer, you might get one of these:


You know what the ironic thing is? Neither of those chairs were designed to be sat in, yet they cost just as much as (if not more than) a decent chair. Those chairs exist due to some insane thought process that managers and executives get nicer chairs to sit in as a person moves up the corporate ladder. I'm sorry, but that's just cruel. If you are going to sit in a chair for 6-12 hours a day, then it had better be comfortable to sit in regardless of who you are. Sitting in the wrong chair for hours on end can and will result in regular headaches and/or migraines (I'm speaking from personal experience here).

So what constitutes a comfortable chair? Leather vs. cloth is usually the first thing people consider. I consider other things. For instance, when I wake up in the morning, I sit in my office chair and put my bare feet on the legs. If the legs of the chair were made of metal, I'd be really annoyed because metal tends to be colder than plastic. Fortunately, the legs of my current chair are made of plastic.

I also lean back in my chair and rest my head. I have what is known as a "high-back" office chair. I measured the back of my current chair as being 28" tall (starting from the inside). I can find fairly cheap chairs that come up to 27" tall. That one inch difference is night and day - I have to tilt my head back uncomfortably to reach the headrest on a 27" chair. To get a 28" back on a chair, I have to go to the "big and tall" section, which immediately adds $250 to the price tag (probably because of hydraulic systems that support heavy people, which I don't need). Unfortunately, the closest chair to my desired measurements that I can find has...metal legs...arg! My $100 decade-old chair beats a $350 chair that's made today. As you can imagine, this is incredibly frustrating AND a waste of my time. Time better spent developing software!

Alright, enough ranting. Onto my wonderfully innovative idea: The ability to craft your own modular office chair from compatible parts. I would love to be able to mix and match:

  • Seat
  • Back
  • Armrests
  • Hydraulic system
  • Legs
  • Rollers
I could buy each part individually and then put the whole thing together myself. I'd be able to put together a $175 office chair that meets all of my requirements in half an hour from a single shopping trip. This isn't rocket science and it is VERY silly that we don't have this yet.

Through my recent experience, I've come to the singular conclusion that one size does NOT fit all. We should all go to our local office supply stores and request that they start carrying modular office chair equipment.

Thursday, March 20, 2014

Why I run Adblock Plus and Ghostery...

A few topics came up on my radar recently that questioned whether or not AdBlock Plus is a security risk because several websites are now asking users to disable it for their website and claimed AdBlock Plus is a security risk. This got me thinking about why I really run both AdBlock Plus and Ghostery. I trust both plugins because they do their job VERY well, are generally trusted products by millions of people, and are open source software. However, the reason I run these tools is not the usual "ads are annoying" or "privacy is important" reasons that I see bandied about. I run them because NOT running these tools introduces security vulnerabilities and serious performance degradation into the web browser stack. Here are a few reasons as to why you should be running *at least* AdBlock Plus:

1) Ad server operators are notorious for running any ad, including ads that deploy malware. It is not uncommon for a hacker to use a stolen credit card to flight malware ads on an ad server platform. They send over their malicious creative and it runs without being analyzed. In some instances, the ad runs before payment even clears! If the flighted ad is placed on what is known as a "remnant ad provider", it can take 6 to 8 hours after discovery of the malware to get it taken offline. Meanwhile, the ad is being served up to all sorts of users around the world. This actually happens and it happens because there is no accountability in the ad server world and the people responsible are reactive instead of being proactive. AdBlock Plus (and, to some extent Ghostery) should be considered to be part of a comprehensive security solution beyond what your anti-virus software and hardware firewall solutions offer. This reason alone should be sufficient to immediately install AdBlock Plus (or equivalent) because, if the ad server can't serve anything in the first place, it can't deliver malware to your computer or other devices. These tools reduce the potential attack surface of the web browser.

2) Excessive web requests. Remnant ad servers are especially notorious for this. To request a single remnant ad position, the browser will generally contact an average of 15 different servers across the Internet. Each server request also requires talking to a local DNS server to get an IP address of the destination. If the local DNS server doesn't know the IP address of the target server (fairly common), it has to go and find out. DNS requests are fairly expensive. Throw 3 to 4 ads on a page and suddenly page load times skyrocket to at least 20 seconds per page. I've personally seen page load times in excess of 60 seconds on modern hardware. AdBlock Plus drops page load times to under 6 seconds in many cases by simply blocking the excessive web requests. Ad server operators don't know when to say "no" to money and constantly make exceptions. Therefore, they don't set rules on request depth and, even if they did, they would never stick to such rules because the drive for money outweighs common sense. I also use Ghostery more for the reason of excessive web requests than the "privacy" reasons that other people use Ghostery for - it shaves off another 1 to 3 seconds per page load with very few issues.

3) Those flighting ads also almost always do not know nor have the desire to know even very basic HTML. They will happily flight ads that output broken content onto the page, which then proceeds to destroy the layout of the page. Mismatched 'div's or other bad HTML code results in half of a page simply not loading or loading properly. It then takes up to several hours to diagnose the problem ad and then the ad finally gets taken down. Meanwhile, users suffer with an unusable website. A more stable website viewing experience is just one more reason to run AdBlock Plus.

4) Most ads are not compliant with the Americans with Disabilities Act. Ads that flash, rapidly change colors, have wild patterns (e.g. optical illusions), or otherwise move on a screen can trigger seizures even in those who have never had a seizure before. These triggers are scientifically proven. Therefore, AdBlock Plus is also a lifesaving medical device and brings website operators into some semblance of compliance with ADA regulations. The only ads that are remotely ADA compliant are those that are static images with muted color combinations. But since you don't know nor can control what ads will be served to you, the only solution is to install AdBlock Plus.

5) Animated ads, especially Flash ads, also dramatically hurt browser performance. Moving DOM elements around on a page causes DOM thrashing (for lack of a better term) and redraw operations at the OS level - combined, they take a lot of CPU power to pull it off and frequently lag. Fortunately, some browser vendors are blocking Adobe Flash by default now, but authors of ad creative are just switching to a "Javascript plus images" method, which doesn't help much. The only solution to this problem is to block all ads until the industry wakes up and realizes that animated ads aren't just annoying, they hurt the performance of the user's web browser.

6) Ad server operators don't demand that all ad creative fit in with their website design. It doesn't seem to matter which ad, they all look ugly and destroy what would otherwise be an elegant website design. This stems from no review process prior to flighting any ad. A good review process will reject both ads and advertisers that refuse to meet a set of well-defined requirements that result in ads that look good in relation to the rest of the website. This lack of concern over the ad creative that users will see demonstrates that there is also a lack of concern over the website's users. If a website operator can't be bothered to properly care for their users by only flighting ads that have been through an extensive review process, then AdBlock Plus is a great way to send the message that the users want to be cared about to the website operator.

7) Third-party server dependencies hurt browser performance. If just one third-party server goes offline in an unusual way, pages that depend on the third-party will never finish loading. A lot of sites depend on the "DOM ready" event to fire to execute important changes to the page. If the browser is waiting on some third-party server to return content before continuing and that server hangs for 30+ seconds, I'll generally just leave and go elsewhere. I've seen both ad servers and analytics servers hang for extended periods of time. AdBlock Plus and Ghostery dramatically reduces the number of third-party dependencies, which speeds up page load times while simultaneously helping improve site uptime.

Until all of these issues are addressed by the entire ad and SaaS industries, AdBlock Plus and Ghostery stay installed and active on my hardware.

Saturday, March 08, 2014

Writing software without copyright still needs a license

Let's say for a moment that you are writing some software that you want to release into the public domain. That is, you don't want to claim that you own a copyright on the software. This is very rare to see in the first place, but it does happen. Interestingly, Copyright Law doesn't do anything but protect others from copying and modifying your work. Neither public domain software nor copyright protected software protects the author from lawsuits from damages arising from use of the software. In other words, you still need a license to protect yourself from liability lawsuits.

Unfortunately, it seems like there aren't any OSI approved licenses for software authors that are prepackaged and ready for use with public domain software. The OSI actually doesn't have such a license because it believes it can't correctly define what Public Domain means to the author within the license itself. I disagree with that assessment.

Having researched numerous licenses over the years, I'm very comfortable with various licenses. The MIT license is, in my non-legal opinion (because I'm not a lawyer), the most liberal open source license that's as close the public domain without it actually being public domain. It basically says, "Hey, you can do whatever you want with this software just don't sue me if it causes harm. However, please note that I own the code and you have to include this license and my claim to copyright somewhere in your software." That last little bit is a sticking point if you don't WANT to claim copyright because you want it to be in the public domain.

Toward this end, here is my best attempt to satisfy the concerns of OSI regarding public domain with a modified MIT license:

Modified MIT License for Public Domain software

Public Domain or legal equivalent
Original authorship by [authors] (the "Authors") in [year]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so.


The first line of the actual agreement ("Public Domain or legal equivalent") is intended to be treated as a title for the agreement, having legal impact wherever it applies (e.g. United States Copyright Law clearly defines what Public Domain means). If a locale has no such definition, the first line will most likely be overlooked and the second line ("Original authorship by [authors] (the "authors") in [year]") says who is the actual owner of the copyright (i.e. a legal fallback mechanism). "Original authorship" is a clever avoidance of the legally defined word "copyright" in many locales.

The first paragraph therefore will only technically apply to those locales where copyright law has no official definition of Public Domain. However, some or all of the terms may apply regardless. In essence, it clarifies the intentions of the author in regards to their hold on copyright. Should copyright still apply, attribution is included with the license to indicate who the claimants actually are. Obviously someone with intention to place their software into the Public Domain has no intention to ever claim ownership of their copyright in the first place. The definition of Public Domain is therefore clearly defined by the first paragraph. Whenever and wherever there may be doubt, define what you mean.

The second paragraph is the "covering your legal rear" paragraph. Of the two paragraphs, this is the most important one because it protects the authors from many types of lawsuits. However, wherever copyright law may still apply (and even those locales where it doesn't), the combination with the first paragraph adds extra protection by clearly specifying that the author allows the software to be used for any purpose, thus removing all legal liability (wherever legally allowed). I removed the words that referenced "copyright holders" since, by definition of public domain, there are no copyright holders. However, the use of '(the "Authors")' in the legal fallback mechanism used earlier means that the word 'AUTHORS' is tied to those who hold the copyright (if any). Even if copyright doesn't apply, the word 'AUTHORS' in that specific location protects the authors from legal liability for the software. This really wraps up the entire package, puts a fancy bow on it, and tells lawyers to go away.

It is my opinion that this license represents the closest to public domain that we as software developers can get legally worldwide while staying really far away from liability lawsuits.

Permission is hereby granted, free of charge, to any person obtaining a copy of this license (the "License", to deal in the License without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the License, and to permit persons to whom the License is furnished to do so.


Hey, gotta cover my legal rear when writing licenses too. Interestingly, the license itself is a kind of circular reference.