Category: George Lerner – Website Tech

Migrated content from website-tech.lc.lndo.site

  • Using PHP Packages without Composer

    Composer http://getcomposer.org/ is a “Dependency Manager for PHP”.

    If you do not have Apache command line access, like most people using a shared hosting account, instructions for installing a PHP package with Composer won’t work (at least not using the instructions given):

    The preferred method of installation is [composer](http://getcomposer.org/). In your project root (not web root), create a minimum [composer.json](http://packagist.org/) file:
    {
    "require": {
    "VerticalTab/Pillow": "x.x.x"
    }
    }
    Replace "x.x.x" above with the tag number you want to use.

    Next, get composer and use it to install (again, in your project root)
    $ wget http://getcomposer.org/composer.phar
    $ php composer.phar install
    This will put the library into your vendors directory.

    Then you would use this line of code in your program to access the package:
    require 'vendor/autoload.php';

    Well, you don’t need Composer to install this package, you can insert some standard PHP code that works, into your program. (But you won’t have all the version checking that Composer does.)

    This PHP code replaces the autoload.php:
    function __autoload($class_name) {
    require_once("./$class_name.php");
    }

    The package I am using as an example is a Zillow API package, in the VerticalTab folder, inside the current folder, and contains these files:
    /VerticalTab/Pillow/Chart.php
    /VerticalTab/Pillow/Comps.php
    /VerticalTab/Pillow/Date.php
    /VerticalTab/Pillow/HttpClient.php
    /VerticalTab/Pillow/Links.php
    /VerticalTab/Pillow/Property.php
    /VerticalTab/Pillow/Proxy.php
    /VerticalTab/Pillow/Range.php
    /VerticalTab/Pillow/ResultSet.php
    /VerticalTab/Pillow/SearchResults.php
    /VerticalTab/Pillow/Service.php
    /VerticalTab/Pillow/Xml.php
    /VerticalTab/Pillow/Zestimate.php

    List all the package’s .PHP files using a special syntax. Convert the names like below. The backslashes are required; forward slashes are incorrect syntax. Drop the .php extension

    use VerticalTab\Pillow\Chart;
    use VerticalTab\Pillow\Comps;
    use VerticalTab\Pillow\Date;
    use VerticalTab\Pillow\HttpClient;
    use VerticalTab\Pillow\Links;
    use VerticalTab\Pillow\Property;
    use VerticalTab\Pillow\Proxy;
    use VerticalTab\Pillow\Range;
    use VerticalTab\Pillow\ResultSet;
    use VerticalTab\Pillow\SearchResults;
    use VerticalTab\Pillow\Service;
    use VerticalTab\Pillow\Xml;
    use VerticalTab\Pillow\Zestimate;

    Now all the package code is accessible to your program.

    Inside the package are namespace commands, e.g. VerticalTab/Pillow/SearchResults.php has namespace VerticalTab\Pillow;

  • PHP Unzip bug makes WordPress Updates hang

    I have been having problems with WordPress Updates for a while. No error messages, just the WordPress Update, or WordPress Plugin Update, hangs.

    Then I found out that the WordPress backup plugin I am using, BackWPUp, failed if I configured it to use any compression.

    Then I found out that the problems I had been having with random-seeming 500 Server Errors was from the WP-Super-Cache plugin configured with, hmm, compression.

    WordPress Updates would create new folders, but no files in them.

    I wrote a PHP program, outside of WordPress, to test if the problem was in WordPress, or in the version of PHP used on my server.

    The problem is in PHP. The PHP unzip routines lock up. No error message on screen, in the PHP log, or in the system log.

    WordPress 3.6.0 and 3.6.1, Multi-Site, Apache, PHP Version 5.2.17, [HTTP_ACCEPT_ENCODING] => gzip, deflate

    PHP itself has a bug in Zip/Unzip in old versions. Hangs without any error message displayed or in logs. Affects WordPress Update, and Plugin/Theme Updates, and any plugins that use compression (e.g. WP-Super-Cache or BackWPup)

    PHP Change Log http://www.php.net/ChangeLog-5.php#5.3.4 says
    PHP Version 5.3.4 released 09-Dec-2010 – Fixed crash in zip extract method

    Note: PHP versions are numbered so version 5.3.10 is higher than 5.3.9 (5.3.1 is lower)

    (more…)

  • XAMPP “Limit Not Allowed” in .htaccess

    Quick Version:
    In \xampp\apache\conf\httpd.conf
    Find each occurrence of AllowOverride None
    Change it to AllowOverride All

    In \xampp\apache\conf\extra\httpd-vhosts.conf
    Make the same change.

    How I Have XAMPP Set Up

    I have XAMPP, a full Apache web server for Windows, installed on my computer. Lets me develop and test web sites where only I can see my mistakes, then upload a working page to the Internet.

    I have my web site files on a USB drive, so if I need to I can easily take it with me.

    I have XAMPP Portable installed on that USB drive (connect the drive to any Windows computer, my testing web server is ready to go). I also have NotePad++ Portable (HTML editor), Filezilla Portable (FTP), and Firefox Portable (browser) with Firebug plugin (lot of features, invaluable for adjusting CSS until looks right, browser updates as I make changes, then copy correct CSS into NotePad++); all the tools I need for modifying web sites on-the-go.

    I have XAMPP also installed on my computer, as a Windows Service, configured to use the web pages on that USB drive. Why both? Because I am nuts. I mean, so I could teach you how to do it that way! There may be some performance advantages to using the non-portable version, and probably some security advantages to using the Windows Service. Knowing how, it certainly isn’t harder to use. And I’m telling you how.

    Change Configuration to Allow .htaccess More Control

    The error messages “Limit Not Allowed” or (Options, DirectoryIndex, IndexIgnore, see http://httpd.apache.org/docs/2.4/mod/core.html#allowoverride for full list) is a configuration issue.

    XAMPP default installation is configured for you as a server administrator, with full control over your testing environment. (XAMPP says clearly the default is not configured for security needed in a production server, but for testing.)

    I want to be able to test the .htaccess files I write, just like clients would have (and like I have) on shared hosting accounts. But the default configuration blocks changing some things in .htaccess, that are better configured in server configuration files if you have access to them. (more…)

  • Example of Error Reporting from Error 403:Permission Denied

    Here are some actual error messages I received, triggered by hackers or spammers. I’ve only edited them to not disclose my server secrets.

    Definitely read the main post Error Reporting from .htaccess.

    Here’s the error log entry:

    htaccess Errors = badRequestString:.asp--asp | 220.161.96.101 Accessing: lcblog.lernerconsult.com, /bokeindex.asp Tue Sep 3 2013 10:44:23 am MST 

    Here is the email subject:

    403 lcblog.lernerconsult.com /bokeindex.asp

    Right away, I know a hacker got blocked, I don’t need to read the email. Delete.

    Here’s what the email contains: What triggered the 403:Permission Denied error (the use of .asp), who tried to load what, info about them including what cookies they have and browser type. Then I can see the system PHP error log, so I know the status of my site; yeah I know I’m sending that via an insecure email. (The 403 Errors are logged in a different file, not cluttering my system error log.) Then the PHP system variables, $__SERVER and $_ENV (these I usually keep commented out of the emails, now that I’ve made sure I have the info I need, and if I’m tracking a big problem, it’s really nice to have).
    (more…)

  • Web Site Development Tools (Windows and OS/X)

    Web Site Development Tools (Windows and OS/X)

    You really should use an FTP program. (No, don’t use your web hosting company’s HTML file browser. Not as fast, easy, reliable. Also, don’t edit plugin or theme files using WordPress built-in editor, you need a programmers editor, and then FTP.) Windows FileZilla or FileZilla Portable; OS/X FileZilla Client or just search for FileZilla on download.cnet.com, it’s completely free and well done. Find out from your hosting provider if you can connect securely (SFTP is most common).

    If you don’t already have a good programmers editor, get Notepad++, Notepad++ Portable, or for OS/X TextWrangler or BBEdit look like good choices (but I haven’t used either). TextWrangler vs BBEdit Comparison. Both are from the same company, so get TextWrangler (free) until you know you Need a feature in BBEdit.

    I have clients who use CoffeeCup Free HTML editor, or who have purchased the full version. Very nice. I haven’t used their other tools, I either already had free tools that work great, or wasn’t interested, but those tools all look well done. http://coffeecup.com/

    Having a development server lets you put only working pages on your public web site. I use XAMPP, a full free Apache server, with PHP, Perl, MySQL, and more, to run on your Windows computer. Even a portable version (install on a USB drive, have a web site development server on any Windows computer). Changes the development flow; instead of making updates with edit-upload-refresh browser-repeat, updates become edit-refresh browser. Small change, but it really adds up. Plus of course, it enforces developing and testing on one server, putting the working product on the production server. Develop WordPress plugins on your notebook, without crashing your production WordPress multi-site setup with 30 client blogs.

    If you want a full professional HTML/PHP/Javascript development environment with debugger, I’ve used NetBeans IDE and like it. Versions for Windows, OS/X and others. Get the “PHP Bundle” unless you know you are going to use other languages; you can always “add or remove packs later using the IDE’s Plugin Manager (Tools | Plugins)”. But unless I am working on something where stepping through the code to find bugs is what I need to do, I find I use NotePad++ most, perhaps just because I learned it first.

    For website development you have to have Firefox, with the Firebug addon. And it’s really good to have Google Chrome too, there are occasional CSS slight differences; mostly I’ve found I was sloppy with CSS sometimes, Chrome would show my sloppiness where Firefox would interpret my CSS to do what I meant. Chrome has the developer tools built in (the Inspector), that look a lot like Firebug; both let you see all the CSS statements are loaded for the element, which are over-ridden vs active, and adjust the CSS until you see the results you want. Both have a lot more, and that’s the feature you’ll use every time something on any page doesn’t look right.

    (I don’t use Internet Explorer except occasionally I quickly check my site displays in an older version; IE9 came on my new computer. Any version of IE older than that gets the same standards compliant, responsive, minimal HTML, formatted by CSS web site that I give to any smart phone, plus a very small IE-specific CSS file that makes it display simply; anyone using old IE today is used to web pages not looking great.)

    I use the Fireshot plugin for screen captures, Firefox/Chrome/IE. Excellent for communicating technical visual things with other people.

    Check out Xplorer2 for file browsing, much easier to have two panes in a single window, than using Windows Explorer. Use bookmarks for your favorite folders, instead of endlessly navigating up and down the tree of folders. (on download.cnet.com)

    For images, I use IrfanView for viewing and minor edits such as cropping and color balance. GIMP is the best free image editor; I barely know how to use it for simple things; graphic artists compare its features to PhotoShop.

    For keeping all my passwords for all those sites and banks and stores, settings for FTP sites, settings for databases, etc, I use KeePass Portable (on download.cnet.com)

    GMail and Google Hangouts have custom email accounts for free, view email from any web browser, forward GMail messages to any POP or IMAP account (e.g. on Thunderbird). Google Hangouts is connected to a GMail account, chat audio or webcam audio/video. Screen sharing, if you do any technical support you can guide the other person as you watch their screen. Completely free calls to any USA phone number.

    VLC Media Player: http://videolan.org/vlc excellent media player capable of reading most audio and video formats.

    7 Zip: http://7-zip.org An open source, free alternative to WinZip, has higher compression, great integration with Windows Explorer or xplorer2.

    Adobe Reader: http://get.adobe.com/reader the standard free PDF viewer.

    Audacity: http://audacity.sourceforge.net for recording & editing audio files, a lot of professional-level multi-track editing capabilities. Easy for trimming audio (e.g. the long pause from starting recording to start of talking), and making people who talked at different volumes sound the same. Open source.

    Evernote: http://evernote.com Scan your notes, receipts, etc, will OCR the content, save it, and make it searchable. Free.

    GIMP: http://gimp.org (free download)
    An open source program used to create & edit – free alternative to Photoshop.

    Open Office: http://openoffice.org (free download)
    Open source office suite software – word processing, spreadsheets, databases, presentations. Reads .csv files better than Microsoft Office (if a text field has multiple lines, MSOffice can’t read it).

    Panda Cloud Antivirus (download.cnet.com) or AVG: http://free.avg.com (free download)
    Anti-virus and anti-spyware protection.

    BrowserShots: http://browsershots.org (free online tool)
    Use this to see how your website look in various browsers, browser versions, operating systems.


    Other Software, Others Recommended

    CamStudio: http://camstudio.org (free download)
    Lets you record all screen & audio activity on your computer and create video files.

    CCleaner: http://ccleaner.com (free download)
    Removes unused files from your pc allowing Windows to run faster and freeing up space.

    Color Cop: http://colorcop.net (free online tool)
    A multi-purpose color picker – great for web designers and programmers.

    Down For Everyone: http://downforeveryoneorjustme.com (free online tool)
    Is your site down? Use this tool to see if your website is down for other people.

    DupeFree: http://dupefreepro.com (free download)
    Quickly check for duplicate content & LSI keywords.

    Give Away Of The Day: http://giveawayoftheday.com (free download)
    Unique site that offers you a free license digital product daily.

    Launchy: http://launchy.net (free download)
    Opens your documents, project files, folders, and bookmarks with just a few keystrokes .

    NicheBot Classic: http://nichebotclassic.com (free online tool)
    Online keyword research tool helps you target the correct keywords.

    OSWD: http://oswd.org/ (free downloads)
    Open Source Web Design offers free web design templates.

    PDF995: http://pdf995.com (free download)
    Use this tool to easily convert files to PSD format.

    PDF to Word Converter: http://pdftoword.com (free online tool)
    Easily create editable Word Doc files from PDF content โ€“ for legit purposes only! ๐Ÿ™‚

    Pixie: http://nattyware.com/pixie.php (free download)
    Great for web designers – just point to a color and it will tell you the code value for that color.

    Private Label Rights Forum: http://resell-rights-weekly.com/forum (free online tool)
    Very helpful forum with lots of good info on internet marketing. Friendly members too.

    Resell Rights Weekly: http://resell-rights-weekly.com/ (free downloads)
    Free membership where you can download over 150 PLR/RR products for free.

    Roboform: http://roboform.com (free download)
    Easily & safely manage your passwords.

    ScreenHunter: http://wisdom-soft.com/sh/sh_free.htm (free download)
    Software that allows you to โ€œcaptureโ€ any part of your desktop, a window or full screen.

    ShortKeys: http://shortkeys.com (free download)
    Allows you to set up replacement text or paragraphs for any user defined keystrokes.

    SpyBot: http://safer-networking.org (free download)
    Effectively detects and removes spyware from your PC.

    Textpad: http://textpad.com (free download)
    A powerful text editor, for editing HTML code.

    TimeLeft: http://nestersoft.com/timeleft (free download)
    A countdown, reminder, clock, alarm clock, stopwatch, timer, & web countdown tool.

    Tweet Deck: http://tweetdeck.com (free download)
    Great for staying in touch with your contacts across Twitter, Facebook, MySpace and more.

    Kompozer: http://kompozer.net (free download)
    An easy to use WYSIWYG HTML editor. Less buggy than NVU (the project was picked up where NVU left off). But doesn’t know anything about PHP, won’t even open a PHP file (copy/paste results into your PHP file, in NotePad++.) As of 9/1/2013: “Latest development version: 0.8b3 (2010-02-28)”, so the project has halted. I recommend get the CoffeeCup Free HTML Editor, which displays the preview in a portion of the screen. Or if you really want to have WYSIWYG web editing, use WordPress!

  • Testing .htaccess Lines Without Crashing Your Site

    On Apache servers, you put many configuration settings in your .htaccess file.

    Syntax Errors in your .htaccess file will crash your entire site. If you have errors in your /public_html folder .htaccess, you will crash all sites set up as sub-domains or parked domains as well.

    This post is not a tutorial on modifying .htaccess files, but on how to test a complex command outside of your main site.

    There are many techniques for identifying which line is the one crashing your site. Your site error log may say a certain command is not allowed in your .htaccess (your hosting provider may have to make the setting change for you). Or you restore a version that you know worked, test it (make sure it still works), and add small chunks of the bad file: works to line 57, crashes by line 100, works to line 70, crashes by line 85, works to 75, crashes by 80, ah it’s line 77. (When narrowing down which chunks work, make sure you keep section tags matched, having unmatched .htaccess section tags will definitely crash your site.)

    Testing Syntax Outside Your Main Site

    Your site is functioning. You want to add something to .htaccess, without crashing the site.

    Before you do anything else, Back Up to a new folder on your computer (very deliberately Not to the folder you use every day for web site work!) your functioning .htaccess file. Then rename it in this special folder, with date and status, and make it a .txt file). Keep calm, keep sane, be clear. Have a working version you can fall back on. Keep your working version safe.

    Test the new command first, before adding it to your main site, someplace with no effect to normal site visitors.

    You could have a .htaccess file in any folder. The .htaccess from the root folder is read first, then any .htaccess files in sub-folders to get to the folder a web page is in. (Don’t replicate all the commands from the root .htaccess, and don’t make a .htaccess in each folder unless you need a change from the folders above, that just slows down your web site.)

    Make a new folder, a sub-folder of your main site. Any made-up valid folder name. Put a minimal HTML file in that folder. Here’s an example:

    
    
        Minimal HTML file
        
    
    
    

    This is a Minimal HTML file for Testing.

    Test that this page displays in your web browser.

    Now put a new, empty .htaccess file in this new folder. (The file name must start with a period, so it may be easiest to use your FTP program to create a new file on the server, and then use your FTP program to open it for editing; most Windows programs don’t understand file names starting with a period…)

    Put a comment (any line that starts with a # is a comment) in this .htaccess file, save it, make sure the test page still displays.

    Now you can begin actual testing of the line that was causing all the trouble.

    In the following example, start with a very simple statement, gradually add a piece at a time and make it work.

    Each line of the example below was how I tested a long User-Agent. Below is the final result, with the bad lines commented out.

    This one works:

    SetEnvIfNoCase User-Agent LinkedInBot itWorked

    This has a slight change, the parenthesis, and fails:

    SetEnvIfNoCase User-Agent LinkedInBot(compatible itFailed

    The failure is a 500 Server Error. (Ignore the itWorked or itFailed environment variables being set, that’s not what the failure is, the failure is an illegal use of a special character in the statement.)
    Change to this and it works again:

    SetEnvIfNoCase User-Agent LinkedInBot\(compatible itWorked

    The backslash converts the meaning of the opening parenthesis from special character to something to display. (Parentheses enclose partial results that can then be referred to, and the missing closing parenthesis makes the syntax invalid, 500 Error.)

    Then add a single line of code and test it (in order to test the bad lines below, you’ll have to remove the leading #… as a comment, of course it’s valid!)

    Notice the difference between each line and the one before it.

    Working Example: Testing for a User Agent

    Ignore the word wrapping of long lines, on this screen and in your text editor. Each command must be on a single line (i.e. the command ends when you press the Enter key). Don’t type line numbers, displayed here only to help you see what is a separate line.

    # actual user agent string:  LinkedInBot/1.0 (compatible; Mozilla/5.0; Jakarta Commons-HttpClient/3.1 +http://www.linkedin.com) 
    
    # .htaccess lines that start with # are comments. Comments can Only start at the beginning of a line.
    
    # If the User-Agent system variable contains the value "LinkedInBot" (not case sensitive), set an environment variable "itWorked":
    SetEnvIfNoCase User-Agent LinkedInBot itWorked
    
    # SetEnvIfNoCase User-Agent LinkedInBot(compatible itFailed
    SetEnvIfNoCase User-Agent LinkedInBot\ \(compatible itWorked
    # must escape an open parenthesis ( by preceding it with a back-slash \
    # must escape a space meant as part of the string (so it isn't separating parameters)
    
    SetEnvIfNoCase User-Agent LinkedInBot/1\ \(compatible itWorked
    # a forward slash / is okay
    
    # SetEnvIfNoCase User-Agent LinkedInBot/1.0\ \(compatible itFailed
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible itWorked
    # must escape a period .
    
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5 itWorked
    # semicolon ; is okay
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0; itWorked
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0;\ Jakarta\ Commons itWorked
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0;\ Jakarta\ Commons- itWorked
    # hyphen - is okay (test if it works when could be part of a character range e.g. a-f)
    
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0;\ Jakarta\ Commons-HttpClient/3\.1  itWorked
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0;\ Jakarta\ Commons-HttpClient/3\.1\ +http  itWorked
    # plus + is okay
    
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0;\ Jakarta\ Commons-HttpClient/3\.1\ +http://www  itWorked
    # colon slashes :// are okay
    
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0;\ Jakarta\ Commons-HttpClient/3\.1\ +http://www\.linkedin\.com  itWorked
    SetEnvIfNoCase User-Agent LinkedInBot/1\.0\ \(compatible;\ Mozilla/5\.0;\ Jakarta\ Commons-HttpClient/3\.1\ +http://www\.linkedin\.com)  itWorked
    # closing parenthesis (at least when isn't an open parenthesis it could match) is okay
    
  • Server alert you of File Not Found and other Errors

    I want to know when there are errors on my site.

    “404: File Not Found” errors can be from forgetting to upload an image, or from typing a URL wrong for a link to another page on my site. They can also be from people typing a file name wrong (some words are commonly mis-spelled). I can correct all of these, if I know about them.

    Most people have to remember to check their site logs. (But they don’t.) I want the option of having the server send me an email.

    Yes, I get emails about made-up names search engines look for, and made-up names spammers search for. I just delete these emails, part of web site maintenance (or I turn off emails about errors that didn’t start from my site, e.g. hackers searching). A few search bots are so bad, I have my site not send me emails about their “missing files”; the Bing-bot is drunk.

    I also want to know about some categories of 403: Access Denied requests, to make sure that none are for files that should be permitted. I’ve had some WordPress plugins, for example, that produced file names with double slashes in them, which my security system was blocking; I needed to know that was going on, and what exactly was making those legitimate file requests get blocked. I needed full disclosure from my server.

    Here is how I have the server give me the reports I need. (Another post will cover the security system I use, and the modifications to get the full disclosure from it.)

    What you Need to Use This

    I’m writing to experienced PHP programmers. You have to be able to understand, install, test PHP programs. This works on my server, LunarPages hosting and an IIS server, but accesses server variables that might be different on your server; there is no way I can know the details of your server (unless you hire me to install this for you). This might not fully work on your server, or might need modification! If you can’t debug PHP, for server-level functions, don’t try this; have me or another programmer do this for you.

    This works best on Apache web servers. This also works on IIS servers, though they will probably give less detailed reporting, and may block writing the error log entries. (I’m not going to give IIS instructions here, if you have an IIS server you probably already know how to adapt these instructions. In another post I’ll show my web.config file changes for IIS.)

    How to Call This

    I have my server error pages, 403.php 404.php 500.php (specified in .htaccess) include errortowebmaster.php. I have all these files in /public_html/shared (and have symbolic links defined so the shared folder literally does get shared by all my web sites).

    Initializing errortowebmaster.php

    
    

    So far, I am doing some standard initializing variables.

    LinkChecker is a good free program to check for broken links on your site; I want to not get an email for each link it finds, since it produces good reports itself. I simplify the http agent field (and have these defined in one place should the agent ever change).

    What Caused 403:Access Denied Errors?

    I am using the 5G Blacklist/Firewall from http://perishablepress.com/5g-blacklist-2013/ to block access by hackers and spammers. It works on commonly used patterns in several server fields, not on constantly-updated IP addresses or constantly-spoofed User Agent fields. I highly recommend reading their pages, so you understand how it works.

    However, the 5G Blacklist needs testing, to make sure nothing on your server gets blocked inadvertently. Another post will show how I customized the 5G Blacklist script; this post shows how I use server variables I set, to give me reports — what phrase triggered what section of the Blacklist.

    if ( (getenv("REDIRECT_noRewrite") !== FALSE) || (getenv("noRewrite") !== FALSE) ){
    	$htaccessErrors[] = 'Module mod_rewrite.c not installed or not enabled';
    }
    if (getenv("REDIRECT_dotHTLocation") !== FALSE) {
    	/* for testing only, which .htaccess file is being triggered? The root or a subdomain? Testing showed that on LunarPages hosting, only the QueryString section of 5G Blacklist needs to be in a subdomain's .htaccess file -- easier to maintain updates with most of the 5G Blacklist in only one place */
    	$htaccessErrors[] = '.htaccess location:' . getenv("REDIRECT_dotHTLocation");
    } elseif (getenv("dotHTLocation") !== FALSE) {
    	$htaccessErrors[] = '.htaccess location:' . getenv("dotHTLocation");
    }
    
    if (isset($_SERVER['REDIRECT_noUserAgent']) || isset($_SERVER['noUserAgent']) ) {
    	$htaccessErrors[] = 'NoUserAgent';
    }
    
    /* root .htacess sets variable, subdomain .htaccess sets REDIRECT_variable, since the root checks first then redirects to the subdomain */
    
    /* variable names must exactly match what shows up in $_SERVER, is case sensitive (e.g. badQueryString not BadQueryString) */
    if (getenv("REDIRECT_badQueryString") !== FALSE) {
    	$htaccessErrors[] = 'badQueryString:'.getenv("REDIRECT_badQueryString");
    	$sendMail = $sendMail ? $emailQueryString : false;
    } elseif (getenv("REDIRECT_REDIRECT_badQueryString") !== FALSE) {
    	$htaccessErrors[] = 'badQueryString:'.getenv("REDIRECT_REDIRECT_badQueryString");
    	$sendMail = $sendMail ? $emailQueryString : false;
    } elseif ($querystring=='QUERY_STRING') {	/* from IIS web.config */
    	$htaccessErrors[] = 'badQueryString';
    	$sendMail = $sendMail ? $emailQueryString : false;
    }
    
    if (getenv("REDIRECT_badRequestString") !== FALSE) {
    	$htaccessErrors[] = 'badRequestString:' . getenv("REDIRECT_badRequestString");
    	$sendMail = $sendMail ? $emailRequestString : false;
    } elseif (getenv("REDIRECT_REDIRECT_badRequestString") !== FALSE) {
    	$htaccessErrors[] = 'badRequestString:' . getenv("REDIRECT_REDIRECT_badRequestString");
    	$sendMail = $sendMail ? $emailRequestString : false;
    } elseif ($querystring=='URL_REQUEST_STRING') {	/* from IIS web.config */
    	$htaccessErrors[] = 'badRequestString';
    	$sendMail = $sendMail ? $emailRequestString : false;
    }
    
    if (getenv("REDIRECT_badUserAgent") !== FALSE) {
    	$htaccessErrors[] = 'badUserAgent:' . getenv("REDIRECT_badUserAgent");
    	$sendMail = $sendMail ? $emailUserAgent : false;
    } elseif (getenv("REDIRECT_REDIRECT_badUserAgent") !== FALSE) {
    	$htaccessErrors[] = 'badUserAgent:' . getenv("REDIRECT_REDIRECT_badUserAgent");
    	$sendMail = $sendMail ? $emailUserAgent : false;
    } elseif ($querystring=='USER_AGENT') {	/* from IIS web.config */
    	$htaccessErrors[] = 'badUserAgent';
    	$sendMail = $sendMail ? $emailUserAgent : false;
    }
    
    if ( getenv("REDIRECT_badRequestMethod") !== FALSE ) {
    	$htaccessErrors[] = 'badRequestMethod:' . getenv("REDIRECT_badRequestMethod");
    	$sendMail = $sendMail ? $emailRequestMethod : false;
    } elseif ( getenv("REDIRECT_REDIRECT_badRequestMethod") !== FALSE ) {
    	$htaccessErrors[] = 'badRequestMethod:' . getenv("REDIRECT_REDIRECT_badRequestMethod");
    	$sendMail = $sendMail ? $emailRequestMethod : false;
    }
    
    if ( (getenv("REDIRECT_badCharsInRequest") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCharsInRequest") !== FALSE) ) {
    	$htaccessErrors[] = 'badCharsInRequest (e.g. CRLF)';
    	$sendMail = $sendMail ? $emailBadChars : false;
    }
    /* testing, don't know yet which .htaccess catches badCharsInRequest problems */
    if ( (getenv("REDIRECT_badCharsInRequestLC") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCharsInRequestLC") !== FALSE) ) {
    	$htaccessErrors[] = 'badCharsInRequestLC (e.g. CRLF)';
    	$sendMail = $sendMail ? $emailBadChars : false;
    }
    
    if ( (getenv("REDIRECT_badCookie") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCookie") !== FALSE) ) {
    	$htaccessErrors[] = 'badCookie';
    	$sendMail = $sendMail ? $emailCookie : false;
    }
    /* testing, don't know yet which .htaccess catches cookie problems */
    if ( (getenv("REDIRECT_badCookieLC") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCookieLC") !== FALSE) ) {
    	$htaccessErrors[] = 'badCookieLC';
    	$sendMail = $sendMail ? $emailCookie : false;
    }
    
    if (isset($_SERVER['REDIRECT_ERROR_NOTES'])) {
    	$htaccessErrors[] = 'Error Notes:'.$_SERVER['REDIRECT_ERROR_NOTES'];
    } elseif (isset($_SERVER['REDIRECT_REDIRECT_ERROR_NOTES'])) {
    	$htaccessErrors[] = 'Error Notes:'.$_SERVER['REDIRECT_REDIRECT_ERROR_NOTES'];
    }
    
    /* badNuisance should be sent directly to bad-webbot.php, display minimal page. Nuisance in QUERY_STRING would arrive here */
    if (isset($_SERVER['REDIRECT_badNuisance'])) {
    	$htaccessErrors[] = 'Nuisance:'.$_SERVER['REDIRECT_badNuisance'];
    	$sendMail = false;
    } elseif ($querystring=='NUISANCE') {
    	/* from IIS web.config */
    	$htaccessErrors[] = 'Nuisance';
    	$sendMail = false;
    } 
    
    if (isset($_SERVER['REDIRECT_badBot']) || isset($_SERVER['REDIRECT_badBadHacker']) ) {
    	/* badBot set in /shared/blackhole/.htaccess, for the George Lerner modified version of http://perishablepress.com/blackhole-bad-bots/ which makes lines to cut/paste into .htaccess */
    	if (isset($_SERVER['REDIRECT_badBot']) ) {
    		$htaccessErrors[] = 'badBot '. $_SERVER['REDIRECT_badBot'];
    		$sendMail = $sendMail ? $emailBot : false;
    	}
    	/* If an IP address gives too many 403 errors, add them to the badBadHacker list in .htaccess. Future enhancement, have this file keep count, make a log file with lines to cut/paste into .htaccess */
    	if (isset($_SERVER['REDIRECT_badBadHacker']) ) {
    		$htaccessErrors[] = 'blacklisted by IP';
    		$sendMail = $sendMail ? $emailHacker : false;
    	}
    } elseif ($querystring=='IP_ADDRESS') {
    	/* from IIS web.config */
    	$htaccessErrors[] = 'blacklisted by IP';
    	$sendMail = $sendMail ? $emailHacker : false;
    }
    
    

    On my server, custom server variables get prefixed with 'REDIRECT_' and I tested both with and without the prefix; this is definitely something that your server could do differently. Check the $_SERVER and $_ENV variables for the actual variable names on your server.

    Very Detailed Error Display for My Computer

    Put your IP addresses (e.g. home computer, work computer, and smart phone) in $myipaddress, set in your secret configuration file (secret because it contains your IP address, which will let baddies see your server configuration). Not "secure" but probably "secure enough"; someone who sees your screen can get anything they want from you; someone who has access to your configuration file has full access to your site. Sometimes is Very useful for debugging to see the server variables, especially when I am configuring a new client's account. Just browse to any non-existent file name on their web site from my computer, and my 404.php shows me (and only me) $_SERVER and $_ENV.

    For any IP address I know, I also display my name for it. For example, don't put the local coffee shop in the "My IP" list, but it makes sense to identify the coffee shop so you know "that was me testing things" or "hmm, I was at the coffee shop, but didn't access That file..." (Pay attention to who can see your screen, even from a distance!) I also put names to the IP address of web site clients, since they will have a lot more errors as they develop new pages on their site, (those few times I notice an ongoing problem and give them the solution, they love it).

    Displaying the Detailed Troubleshooting & Error Information

    Note: For getting this to display well in WordPress, I changed <pre> to <code> (open and close tags) in the next section. You'll probably like the display better if you use pre in your code.

    if (in_array($ip, $myipaddress)) {
    	echo "
    .htaccess Errors:
    "; print_r($htaccessErrors); echo "
    "; echo "
    _SERVER:
    "; print_r($_SERVER); echo "

    _ENV:
    "; print_r($_ENV); echo "

    "; if (isset($_GET)) { foreach ($_GET as $arg) { $clean = filter_input(INPUT_GET, $arg, FILTER_SANITIZE_STRING); // new PHP function print "Get Parameter: $clean
    "; } } if (isset($argv)) { // IIS (Cart32) leaves in $_SERVER['QUERY_STRING'], or $_SERVER['argv'], both unparsed, on error pages. foreach ($argv as $arg) { $clean = filter_input(INPUT_GET, $arg, FILTER_SANITIZE_STRING); print "Argv Parameter: $clean
    "; } } else if (isset($_SERVER['argv'])) { foreach ($_SERVER['argv'] as $arg) { $clean = filter_input(INPUT_GET, $arg, FILTER_SANITIZE_STRING); print "_SERVER argv Parameter: $clean
    "; } } } $ipText = $ip; switch ($ip) { case "12.345.67.89" : case "123.45.678.90" : $ipText .= " (My Home)"; break; case "98.76.54.32" : $ipText .= " (My Work)"; break; default: // $ipText .= " (unknown)"; } $combine = $ipText . " tried to load \n";

    Writing an Error Log

    After some checking for uninitialized variables, write a brief error result to log files. I deliberately keep 403 errors out of the system PHP error log, as they require different attention. (Tempting to move 404 errors to their own file, so many idiot search engines/bots, but then I wouldn't see when I forgot to upload an image... I do, however, note when a request originated from a file on my server, since this is much more likely something I need to fix.)

    if (strpos($requri, $servname) !== false) {
    	/* MVC's IIS server gives full URL and status in requri */
    	if ($status !== '') {
    		$subject = $status.' '.$requri; 
    	} else {
    		$subject = $requri;
    	}
    	$combine .= " $requri";
    } else {
    	$subject = "$status $servname $requri";
    	$combine .= " $servname$requri"; 
    }
    
    $mycookie = "";
    foreach ($_COOKIE as $cookie_name => $cookie_text)
    {
    	$mycookie .= "\t" . $cookie_name . ": " . $cookie_text . " \n";
    }
    
    if (!isset($browser_name)) { $browser_name = "(unknown) "; }	
    // set by script from http://techpatterns.com/downloads/php_browser_detection.php
    if (!isset($browser_ver)) { $browser_ver = "(unknown) "; }
    

    Display Info On Web Browser

    $message = "

    $note

    \n

    $combine

    \n

    Remote Host = $remoteHost

    \n

    Query string = $querystring

    \n

    HTTP Referer = $httpref

    User Agent = $httpagent

    \n

    $today

    \n

    Cookies =

    $mycookie
    \n
    "; $message .= "\nBrowser detected as: " . $browser_name . "version " . $browser_ver; $message .= "\nIP Address: " . $ip; echo $message;

    Sending an Email to Site Administrator

    The email will be sent to the address in $adminemail, defined in your secret configuration file.

    function implode_r($glue,$arr){
            $ret_str = "";
            foreach($arr as $a){
                if ($ret_str === "") {
    				$ret_str .= (is_array($a)) ? implode_r($glue,$a) : $a;
    			} else {
    				$ret_str .= (is_array($a)) ? implode_r($glue,$a) : $glue . $a;
    			}
            }
            return $ret_str;
    }
    if (!isset($logfile)) { 
    	$logfile = getenv("DOCUMENT_ROOT") . "/error_log";
    }	
    if (!isset($deniedLogfile)) { 
    	$deniedLogfile = $logfile.'403';
    }
    if (!file_exists($logfile)) {
    	$fh = @fopen($logfile, 'w'); 
    	if ($fh !== FALSE) { fwrite($fh, "ErrorToWebMaster New Error File\n"); fclose($fh); }
    }
    if (!file_exists($deniedLogfile)) {
    	/* some servers prohibit fopen, so @ before it to prevent error messages */
    	$fh = @fopen($deniedLogfile, 'w'); 
    	if ($fh !== FALSE) { fwrite($fh, "ErrorToWebMaster New 403 Error File\n"); fclose($fh); }
    }
    if (isset($htaccessErrors)) {
    	$htaccessErrorsStr = implode_r(" | ",$htaccessErrors);
    	/* Cart32 wimpy IIS hosting doesn't allow error_log ("permission denied") so @ before it */
    	@error_log("htaccess Errors = $htaccessErrorsStr | $ipText Accessing: $servname, $requri $now \n",3,$deniedLogfile);
    } else {
    	@error_log("$ipText Accessing: $servname, $requri $now \n",3,$logfile);
    	$htaccessErrorsStr = '';
    }
    
    /* $message was for on screen, $message2 is for emailing */
    $message2 = "htaccess Errors = $htaccessErrorsStr\n
    $combine \n
    Remote Host = $remoteHost\n
    Query string = $querystring \n
    HTTP Referer = $httpref\n
    User Agent = $httpagent \n
    $today \n
    Cookies = \n$mycookie \n";
    
    $message2 .= "\nBrowser detected as: " . $browser_name . "version " . $browser_ver;
    
    $fh = @fopen($logfile, 'r');
    if (filesize($logfile) == 0) {
    	$theData = "";
    } else {
    	$theData = fread($fh, filesize($logfile));
    }
    fclose($fh);
    $message2 .= "\n\n[Error Log]: $logfile\n" . $theData;
    $message2 .= "\n[End of Error Log]\n";
    $message2 .= print_r($_SERVER,true);
    $message2 .= print_r($_ENV,true);
    
    $to = $adminemail;
    $from = "From: " . $adminemail . "\r\n";
    
    if (isset($_SERVER['REDIRECT_ERROR_NOTES']) ) {
    	$sendMail = false;
    } else $sendMail = true;
    
    if ( (stristr($docroot,"xampp") == FALSE) && ($requri != "/wp-login.php") && ($httpagent != 'LinkChecker/') && ($httpagent != 'LinksManager.com_bot') ) {
    	if ($sendMail !== false) {
    		mail($to, $subject, $message2, $from);
    	}
    }
    ?>
    

    Questions?

    Post your comments or questions here, and of course if you would rather I set this up for you and get it working on your web site, let me know. Contact Me.

  • Starting and Stopping Xampp (Apache, MySql) in Windows 7

    There is a lot of confusion on the support boards, people saying they can’t get Xampp to stop, there “must be a bug in Xampp”, or they “had to kill the application with Task Manager”. No, they are trying an incorrect method for closing a Windows Service.

    Some versions of Windows (including Windows 7) prevent applications from starting or stopping services; you should use the Windows Services program to manage services.

    In the “Xampp Control Panel Application”, you specify whether to run Apache and MySql as Windows Services (by checking the “Svc” box).

    If Apache *is not* running as a Windows Service:
    you use the Xampp Control Panel Application to stop and start Apache.

    If Apache *is* running as a Windows Service:
    you use Windows Services to restart Apache. (Start button, search for Services; or Start, Control Panel, Administrative Tools, Services). Click on the Apache service (the version of Apache you are running will be in the service name), then click “Restart the service”.

  • WordPress missing Post Editor (and other Ajax features)

    My WordPress installation was not showing the Post Editor. Basically instead of an editor, I had a box that I couldn’t type in.

    I also noticed widgets that had drag and drop configurations, where I couldn’t do any dragging. On the “Add New Post” page, the “Add New Category” popup wouldn’t work. The Permalink “Edit” button doesn’t work.

    Okay, something with my WordPress installation wasn’t running jQuery or Ajax properly.

    There is a WordPress plugin that lets visitors to your site load Google’s cached version of jQuery, instead of loading it from your site. Faster for them (since it is probably in their browser cache already). Just install the Use Google Libraries plugin.

    There is a page on why you should let Google host jQuery for you, http://encosia.com/3-reasons-why-you-should-let-google-host-jquery-for-you/

    I have WordPress Multi-Site installed, so I went to the My Sites, Network Admin, Sites page, and from there to Plugins, Add New. I turned on the plugin for all sites (“Network Enable” ), and my editor and Ajax features (used a lot throughout WordPress) work now.

    I also copied jQuery and TinyMCE from a fresh download of WordPress to my site. They are in your wordpress\wp-includes\js\ folder, and go to the corresponding folder on your site. I used FileZilla’s Upload “if different size or source newer” option. (If that doesn’t fix the problem, try copying the entire folder, overwriting all files.)

    The Post Editor is working, both Visual and Text modes. The “Add New Category” link doesn’t work yet. The Permalink “Edit” button is not working yet.

  • Migrate All Your WordPress Blogs into Multi-Site and You’ll Save Lots of Time!

    Unlike most “solutions”, I bring over all your settings, configurations, customizations. Your blogs will look Exactly like before, and you’ll have much less administrative work.

    If you are considering using WordPress Multi-Site, you probably have some blogs already. In fact, you probably have some blogs with a Lot of posts.

    (If you’re just starting, WordPress Multi-Site is still good, it makes having more than one blog much easier.)

    Each blog most likely has a few widgets, and probably some settings on each post and page. Maybe you specified what image should be featured on the post. If you are marketing at all, you should have a custom search engine description and keywords for each post. Maybe you selected some Amazon books that go well with this page. Your sign-up form. Your latest Facebook or Twitter posts.

    When you want to bring blogs from WordPress (single site) into Multi-Site, you find there are tools to help with that. “Great, this will be easy”, you think. Then you find that only the text of your posts and pages and comments are migrated. (Unlike if you have me migrate your blog!) (more…)

  • HTML on-page links not working

    HTML bookmarks, or on-page link targets, are used often so viewers can easily jump to another section on a page. The link would be in this form: <a href="#hello">. Then define the bookmark with code like this: <a name="hello">

    These links have stopped working if you use the wrong HTML code. Used to be you could use <a name="hello"></a> or <a id="hello"></a> but you can now only use the id attribute for this.

    name= is deprecated for link targets, and invalid for links in HTML5. It no longer works at least in latest Firefox (v13). Change <a name="hello"> to <a id="hello">

    The target does not need to be an <a> tag, it can be <p id="hello"> or <h2 id="hello"> etc. which is often cleaner code.

    name= is still used (needed) in forms. It is also still needed in META tags.

  • Moved Hard Disk to New Computer, Folders “Access Denied”

    Even creating a Windows User of the same name and password as on the old computer, I couldn’t access parts of the drive, for example “My Documents”. The password uses a security id, and the security ID for the user has changed.

    http://support.microsoft.com/kb/810881 has the solution:

    1. Turn off Simple File Sharing (Security tab, used below, won’t display with this on):
      1. On the Startmenu, click My Computer.
      2. On the Tools menu, click Folder Options, click the View tab.
      3. Under Advanced Settings, click to clear the Use simple file sharing (Recommended) check box, click OK.
    2. Right-click the folder that you want to take ownership of, and then click Properties.
    3. Click the Security tab. If a Security message appears, click OK.
    4. Click Advanced, and then click the Owner tab.
    5. In the Name list, click your user name, Administrator if you are logged in as Administrator, or click the Administratorsgroup.Take ownership of the contents of that folder: click to select the Replace owner on subcontainers and objects check box.
    6. Click OK. You may receive the following error message, where Folde ris the name of the folder that you want to take ownership of: “You do not have permission to read the contents of directory Folder. Do you want to replace the directory permissions with permissions granting you Full Control? All permissions will be replaced if you press Yes.”
    7. Click Yes.

    Now you are the owner of that folder.