WISHLIST / TODO: Add merging support to COMCollection. Add the ability to load COMCollection from arrays in various ways. Add the ability to export COMCollection to an array. Change CCacheNode to inherit from CAVLTreeNode and not instantiate a new object for it. Figure out why COMCollection is leaking memory when IIS exits instead of emptying itself as it is designed to do. VERSION 8.0.0.0 Changed the license from the LAF LGPL to the GNU LGPL. Fixed a small problem in CMutexQueue::~CMutexQueue that could cause an access violation while destroying an exclusively locked lock. VERSION 7.1.1.0 - 8/25/2004 Changed CMutexQueue::set_spinlock_sleep_msecs() to pass the new sleep time value to big_lock_lock() when trying to lock the internal lock. Otherwise, if the first attempt to use the lock is to set the sleep time and the big lock fails, the big_lock_lock() will spin forever. Duh. Changed a number of places in CMutexQueue that assumed a thread ID was a simple type like an int. In Mac OS X, it is a pointer and direct equality comparisons are meaningless. Fixed a bug in CMutexQueue::clean_nodes() that was passing NULL to pthread_getschedparam() in place of two pass-by-reference parameters. Linux's pthread library will ignore the NULL parameters but Mac OS X's pthread library will not -- it produces bus errors instead. VERSION 7.1.0.0 - 9/2/2003 Changed CMutexQueue::get_queue_size() and CMutexQueue::get_queue_status() to optionally accept a parameter to make their lock attempts time out after a number of milliseconds. Fixed CMutexQueue::unlock() to honor the ignore_thread_ownership flag on non-exclusive locks by finding the first lock and unlocking it. This only makes sense when all of the threads using the lock have locked without thread ownership. Added CMutexQueue::set_user_data() and CMutexQueue::get_user_data() to allow the mutex to hold one piece of user-specified data. This data is protected from simultaneous updates by the mutex's internal lock but accesses are not guaranteed to be first-come-first-served as with the lock()/unlock() requests. Added a new version of CMutexQueue::get_queue_size() that returns a return_code value. Changed most of the functions in CMutexQueue so they first check to see if the destruct_pending flag is set and abort if it is. Added a new class, LAF_semaphore, to provide a simple thread-safe semaphore utility. It also provides a get_/set_user_data() feature like CMutexQueue. VERSION 7.0.0.0 - 8/19/2003 Changed CMutexQueue::big_lock_lock() so it will make a weak attempt to discover if its internal state is valid and will also optionally watch for the timeout to expire while spinning. This may help locate the cause of some out-of-control thread symptoms that have been seen in production (caused by other bugs but manifested in big_lock_lock()). Fixed a nasty little bug in CMutexQueue::unlock() that could cause access violations under certain conditions. Changed CMutexQueue::lock() and CMutexQueue::upgrade() so they will clean out stale locks held by threads that have exited without releasing their locks. Added a third parameter to CMutexQueue::lock() to be set by threads that are grabbing locks that must remain locked even if the thread dies. The new parameter sets a flag on the lock that prevents it from being broken when the thread exits. This is necessary for locks that function as delay timers for worker threads. This change breaks backwards compatibility. VERSION 6.9.0.0 - 6/25/2003 Added validation types "time" and "hhmm" to form_validations.js, along with some conversion routines. Changed validate_isEmail() in form_validations.js to allow dots ('.') in email addresses. According to RFC 822, they're not legal but so many ISPs and companies use them that they've got to be able to pass the form validation. Changed case_sensitive_compare_variant_ptrs(), case_insensitive_compare_variant_ptrs(), case_sensitive_compare_md_variant_ptrs() and case_insensitive_compare_md_variant_ptrs() to attempt to compare their parameters as numbers if strings cannot be extracted from them. This allows CMisc::Quicksort() to sort arrays of numbers instead of just strings. The results of sorting an array of mixed types (strings and numbers) is undefined. VERSION 6.8.0.0 - 3/28/2003 Added GetDATEFromVariant() to reusables_atlcom.*. VERSION 6.7.0.0 - 3/13/2003 Added the "money" type to form_validations.js to validate dollar currency fields. Fixed validate_isFloat() and validate_isInteger() in form_validations.js to correctly validate numbers with commas. Fixed validate_isEmail() in form_validations.js to correctly validate email addresses. Added an overloaded version of CAVLTree::remove() that will take one parameter, a CAVLTreeNode*, and remove it from the tree if it is found. VERSION 6.6.2.0 - 2/12/2003 Fixed a problem in CSMTP::CSMTPQueue::run_queue() that was causing multiple asynchronous threads to consume all available CPU while waiting for the queue mutex to unlock. There was no sleep() command issued when the call to upgrade() failed; the thread would just retry upgrade() as fast as possible. VERSION 6.6.1.0 - 1/6/2003 Changed CObjectPool::get_object() to run any newly created objects through the check_object (or check_object_with_return) function before returning it. Changed validate_isEmail() in form_validations.js to allow quoted usernames in email addresses and disallow special characters outside of quoted usernames. Added new parameters to validate_checkone() and validate_checkall() to allow the caller to specify if select() and focus() are to be called on the input element if it fails validation. This can be important when the form has onFocus() or onBlur() handlers. VERSION 6.6.0.0 - 10/23/2002 Added the ability to CMisc::Quicksort to sort two-dimensional arrays. It now takes two additional (optional) parameters, the sort dimension and the sort index. Removed a contradictory copyright notification from the top of form_validations.js. VERSION 6.5.7.0 - 9/24/2002 Fixed a critical bug in CMutexQueue::upgrade() -- if an upgrade attempt failed, upgrade() was not relocking with nonexclusive access. Also, if the upgrade attempt times out and another thread has called upgrade() after the timed out thread did, relocking with nonexclusive access is not possible (it would require more waiting). In that case, the lock has been broken and the caller must not call unlock(). Changed CMutexQueue::unlock() to take no action if the caller is not one of the current lock holders. This is to help deal with the issue of a failed upgrade() attempt that broke the lock where the caller attempts to unlock() anyway. unlock() now accepts a parameter that will override the caller checking and force the unlock of another thread's lock. This is usually only important if CMutexQueue is being used to control worker thread sleeps (mercifully rare). Everybody else should not notice this change. Removed some debugging code from CComCollection and core files. VERSION 6.5.6.0 - 9/18/2002 Marked CAVLTree::~CAVLTree(), CCache::~CCache(), CCache::~CCacheNode(), CHeap::~CHeap(), CObjectPool::~CObjectPool() and CStack::~CStack() virtual to force decendant classes' destructors to fire when the objects are deleted. This closes a number of memory leaks observed in production, both in LAFcommon and in other projects. Added CCacheNode::empty() and changed CCacheNode::~CCacheNode() to call it. This will allow derived classes' destructors to also clean up the CCacheNode structure. Changed CCOMCollection::CCOMCollectionSharedNode::~CCOMCollectionSharedNode() to call CCacheNode::empty(). Added CHeap::empty() and changed CHeap::~CHeap() to call it. This will allow derived classes' destructors to also clean up the CHeap structure. VERSION 6.5.5.0 - 9/11/2002 Fixed two serious errors in CMutexQueue::unlock() and CMutexQueue::downgrade() caught by BoundsChecker that could put the node pointed to by newest_node into the saved_nodes list. This allowed the node (or other newer nodes) to be freed by another function when they are still in use by CMutexQueue::lock() or CMutexQueue::upgrade(). Fixed generate_guid(), generate_short_guid() and generate_nonsequential_id() to return NULL if any of the Win32 RPC functions return a non-success code. Extended CObjectPool to allow the functions provided by the caller to accept an additional parameter of void** through which they may return additional information. Since CObjectPool is most often used for database connection pools, this provides a way to pass back exception messages. VERSION 6.5.4.0 - NOT RELEASED Fixed cgi_variable_append() to behave correctly when there is no data to be read from stdin -- fread on Win32 blocks when given a format of "%0c" and there is no available data. Removed some illogical cruft from CMutexQueue::upgrade(). VERSION 6.5.3.0 - 8/7/2002 Fixed a set of typos in form_validations.js that was preventing the correct validation of credit card numbers. Fixed cgi_variable_append() to make it possible to correctly retrieve POSTed variables in CGI scripts. Fixed some of the #include directives in the reusables files so they will compile in non-MFC Win32 projects. VERSION 6.5.2.0 - 6/27/2002 Fixed the CMutexQueue::~CMutexQueue so its new thread accounting system will also unlock dead exclusively locked threads. VERSION 6.5.1.0 - 6/25/2002 Changed CMutexQueue::lock() slightly to return fail_destruct_pending to callers who were waiting on a queue and whose turn had come up when the queue was marked for destruction. Changed form_validations.js to ignore invalid form element objects. Reworked some of CMutexQueue's internal data structures to perform better accounting of which threads are waiting on locks. This allows the CMutexQueue destructor to check if the waiting threads are still active before allowing the queue destruction to complete. This is necessary because some execution environments kill all the threads without notifying them properly so they can clean up their resources. Thanks a lot, Sun! Changed CMutexQueue to use nanosleep() instead of sched_yield() under Linux because nanosleep() actually yields the remainder of the thread's timeslice while sched_yield() does not (according to Ed Bradford @ IBM). VERSION 6.5.0.0 - 6/3/2002 Added a new function to form_validations.js to allow a known element's validation properties to be updated named validate_updateelement(). Moved the documentation out of form_validations.js into form_validations_docs.txt because the size was becoming significant. Changed the types "datetime", "mssqldatetime" and "cdatetime" in form_validations.js to not require a time but to assume midnight if no time is given. VERSION 6.4.0.0 - 5/15/2002 Changed validate_checkall() in form_validations.js to optionally accept an additional argument: the form object to check. If the argument is given, only the form elements contained by the form object will be validated. This is necessary for a page that contains multiple, unrelated forms that require validation. Added reusables_CGI.* to provide some basic CGI input handling routines. Fixed several serious errors in reusables_ObjectPool.cpp that were using CMutexQueue locks without timeouts and always assuming that the object creation function would succeed without checking its returned value. These bugs could cause deadlocks or access violations in database connection pools if attempts to create new connections failed for any reason. Fixed a #define problem in reusables_MutexQueue.h so it will compile under Linux. VERSION 6.3.2.0 - 4/9/2002 Changed html_canonicalize() and html_canonicalize_no_break() to not replace "|" with "¦" because "¦" does not display as the correct character in any browser. It instead displays a vertical bar with a different ASCII code that is illegal in javascript code. VERSION 6.3.1.0 - 4/8/2002 Changed javascript_canonicalize(), html_canonicalize() and html_canonicalize_no_break() not to replace unprintable characters with '?' since every ASCII character is allowed or replaced. Fixed javascript_canonicalize() to handle a NULL source string without generating an access violation. VERSION 6.3.0.0 - 4/3/2002 Added a parameter to GetBSTRFromString() to allow the caller to specify the length of the input string, in case it contains embedded nulls. VERSION 6.2.0.0 - 3/20/2002 Added the field types "cdate" and "cdatetime" to form_validations.js to check values that are to be converted to time_t values. Changed GetTimeTFromDATE() to check the converted value against the bounds of time_t. CTime::CTime() asserts when asked to deal with a date value that is out of bounds for time_t. VERSION 6.1.1.0 - 3/14/2002 Fixed sql_canonicalize_like() to do more than throw access violations. Add GetTimeTFromVariant() to reusables_atlcom.*. VERSION 6.1.0.0 - 3/12/2002 Fixed identical typos in validate_isVisa(), validate_isMasterCard(), validate_isAmericanExpress(), validate_isDinersClub(), validate_isDiscover() and validate_isJCB() in form_validations.js. Added a #include directive to reusables_guid.h to provide a definition for NULL. Fixed replace_text() in reusables_c.cpp to correctly handle a NULL source. Added sql_canonicalize_like() to reusables_c.cpp to canonicalize strings for SQL LIKE clauses. VERSION 6.0.0.0 - 2/12/2002 Added putref_Item() to CCOMCollection. Apparently a property cannot accept an object reference without being declared as "propputref" in the IDL file. All along, LAFcommon.COMCollection has been unable to store COM objects because its Item property didn't have a "propputref" version. I've managed figured out the reasons behind most of IDL's braindamagedness, but this one is beyond me. The only way to accept an object reference through COM is by using a VARIANT (IObject*s don't count as references). VARIANTs are accepted by "propput" properties as long as they don't contain object references. WHAT'S THE DIFFERENCE? A VARIANT's a VARIANT's a VARIANT! This is just stupid. Changed the behavior of GetEmptyFromVariant() not to use VariantChangeType() to coerce VARIANTs of types VT_UNKNOWN, VT_DISPATCH, VT_BYREF|VT_UNKNOWN or VT_BYREF|VT_DISPATCH to VT_EMPTY. Apparently, anything is coercable to VT_EMPTY and VariantChangeType() just sets the destination VARIANT VT_EMPTY instead of running the Value property of the referenced object. Unfortunately, GetEmptyFromVariant() is used in a great number of places, so it may take a while to ensure this change does the Right Thing. VERSION 5.4.3.0 - 12/18/2001 Fixed a serious problem in CAVLTree::insert(CAVLTreeNode*) that was not clearing the avl_children array or resetting the avl_height field. This is an issue when the node has been inserted into a CAVLTree object in the past and its child pointers are not NULL and its height is not 0. This can cause anything from infinite loops to access violations. VERSION 5.4.2.0 - 12/12/2001 Added GetUnsignedCharFromVariant() to reusables_atlcom.* to get an unsigned char from a VARIANT. VERSION 5.4.1.0 - 12/10/2001 Removed all of the "helpstring" entries from the IDL file. The typelib is a stupid place to store documentation and comments like "property Name" and "method Save" don't help anyone anyway. Removing the strings reduced the size of the DLL by 4096 bytes. Changed email.asp and flash_email.asp to accept multiple target email addresses. Changed flash_email.asp to perform marker substitution in the subject line and accept "apparently from" and "apparently to" information like email.asp. VERSION 5.4.0.0 - 11/26/2001 Added the value disabled_for_destruct to CMutexQueue::mutex_statii to allow a caller to determine if the queue has been disabled without having to attempt to lock it. VERSION 5.3.7.0 - 11/19/2001 Changed the list of targets and replacements in html_canonicalize() and html_canonicalize_no_break() to replace all "extended" ASCII characters with their HTML encoding instead of a question mark. This should fix the problem of cut-n-pasted text from MS Word that contains "smart quotes" showing up full of question marks. VERSION 5.3.6.0 - 11/16/2001 Fixed a serious problem with CMutexQueue::~CMutexQueue that was causing access violations when the mutex queue was destroyed while still locked and waiting for the locks to be released. Fixed a problem with CMutexQueue::~CMutexQueue that wasn't releasing the queue's big lock, causing any new attempts that began before the object's destruction was complete to stall while trying to lock the queue's big lock. Fixed CMutexQueue::lock() to honor the destruct_pending flag even when spinning on the lock isn't necessary. Changed CMutexQueue::lock() and CMutexQueue::unlock() to make the default timeout -1 msecs instead of 0. A timeout of 0 msecs now indicates no wait at all and provides a fast path if the lock/upgrade attempt is unsuccessful (no sleeping). Added set_max_queue_size() and set_spinlock_sleep_msecs() to CMutexQueue to allow the maximum queue size and the spinlock sleep time to be set outside the constructor. Fixed CSMTP::CSMTPQueue::generate_script() to encode messages slightly differently. Instead of trying to encode into quoted-printable format and replace lone periods with double periods (..) in the same pass, two passes are now performed. This is necessary because encoding into quoted-printable may word-wrap a period onto its own line. However, because replace_text() regards the line break as part of the previous replacement, it does not notice the lone period and does not replace it. This was causing the SMTP server to return error codes when the message appeared to end prematurely. This bug was discovered by Lonnie Pryor. Replaced all instances of AfxBeginThread() with _beginthread(). After reading online (and much debugging within LAFspam), AfxBeginThread() uses the MFC library to start and control its threads. _beginthread() uses the CRT (C RunTime). Thus, AfxBeginThread() requires the both MFC library and the multithreaded C library while _beginthread() requires only the multithreaded C library. If MFC is not active/available when AfxBeginThread() is called, the process hangs. If the multithreaded C library is not available when _beginthread() is called, an error is returned. VERSION 5.3.5.0 - 11/8/2001 Fixed potential problems in base64_decode(), generate_guid(), CAVLTree::absorb() and CAVLTree::traverse_postorder() where the return variable could be returned without its value having been set. Fixed a potential problem in replace_text() where next_match_num could be used without having been initialized. Fixed a potential problem in quicksort() where pivot_element could be used without having been initialized. VERSION 5.3.4.0 - 10/25/2001 Cleaned up CAutoArray's constructors. Fixed CAutoArray so it will compile under Linux. Fixed CCache so it will compile under Linux without warnings. Cleaned up CObjectPool's constructors. Fixed CObjectPool so it will compile under Linux. Cleaned up CQueue's constructors. Fixed CQueue so it will compile under Linux. Fixed reusables_guid.* so it will compile under Linux. Fixed CSMTP so it will work under Linux. VERSION 5.3.3.0 - 10/15/2001 Changed email.asp and flash_email.asp to send their mail through smtp-script.lookandfeel.com by default instead of email.lookandfeel.com. Changed flash_email.asp to URL encode all the text in its response and not to place a line break before the first response character. Fixed GetBSTRFromString() to use malloc() instead of _alloca() when the source string passes a #defined threshhold. Using _alloca() unconditionally was causing stack overflows with large strings. VERSION 5.3.2.0 - 10/11/2001 Fixed a(nother) bug in replace_text() that was introduced in version 5.1.0.0: if the shortest target is the same length as the longest replacement, the space allocated for the destination string is one character too short. Fixed a bug in replace_text() that has existed since was first written: if the source string is shorter than the shortest target and the shortest target is shorter than the longest replacement, only one byte will be allocated for the destination string. Increased the time CSMTP will wait for an SMTP server response during mail delivery to better cope with slow/overloaded servers. VERSION 5.3.1.0 - 10/8/2001 Cleaned up reusables_c.* to make it compile under Linux. Cleaned up reusables_MutexQueue.* to make it compile under Linux. Fixed LAFCOMMON_SMTP_NAMES_ACCOUNTABILITY_CODES and LAFCOMMON_SMTP_VALUES_ACCOUNTABILITY_CODES in reusables_SMTP.cpp so CSMTP::constant2value() will find accountability code names correctly. VERSION 5.3.0.0 - 9/13/2001 Included limits.h from reusables_MutexQueue.h so it would compile when included in other projects. Added CMutexQueue::disable_for_destruct() to allow a caller to announce its intention to destroy the queue so new lock attempts will fail and return CMutexQueue::fail_destruct_pending. Changed all the uses of CopyMemory() in reusables_SMTP.cpp to memcpy(). Added the macro definitions REUSABLES_GUID_STRLEN_GUID, REUSABLES_GUID_STRLEN_SHORT_GUID and REUSABLES_GUID_STRLEN_NONSEQUENTIAL_ID to reusables_Guid.h to remove any assumptions about the length of strings returned from the GUID functions. Cleaned up CHeap's constructors. Fixed a potential bug in CHeap::init_heap that would try to dynamically allocate 0 bytes of memory. Changed replace_text() in reusables_c.* to accept a pre-declared buffer area to store its result into rather than unconditionally declaring its own. Changed generate_guid(), generate_short_guid(), generate_nonsequential_id(), convert_short_guid_to_guid(), convert_guid_to_short_guid() to accept a pointer to a previously allocated memory area in which to store the result instead of unconditionally allocating new memory. Fixed matching bugs in CMutexQueue::lock() and CMutexQueue::upgrade() that could potentially gain a lock and timeout without realizing the lock had been aquired. The code was also slightly restructured and simplified. Fixed a serious error in CMutexQueue::upgrade() that would correctly upgrade the lock but would not update the newest_node pointer if the thread was forced to wait on the upgrade. If a call to CMutexQueue::lock() came while the upgrade()ing thread was waiting, the stack would be corrupted and the upgrade()ing thread would deadlock forever (or timeout). Declared CAVLTreeNode::~CAVLTreeNode() as a virtual function so it would be correctly overridden by inheriting classes. VERSION 5.2.4.0 - 8/15/2001 Fixed the comment blocks at the top of form_validations.js, email.asp and flash_email.asp to format them correctly for the respective language so they wouldn't generate errors. Changed GetEmptyFromVariant(), GetErrorFromVariant(), GetBSTRFromVariant(), GetStringFromBSTR(), GetStringFromVariant(), GetNumberFromVariant(), GetNumberTypeFromVariant(), GetObjectFromVariant(), GetArrayFromVariant(), GetArrayTypeFromVariant(), GetBooleanFromVariant(), GetDateTimeFromVariant(), GetBSTRFromString(), GetDATEFromTimeT() and GetTimeTFromDATE() to fail gracefully if passed a NULL pointer. VERSION 5.2.3.0 - 8/6/2001 Fixed replace_text() again so it would not throw an access violation whenever it is called with a set of targets of which some are substrings of others. Fortunately, replace_text() isn't very big, so there can't be many more "I.R. Baboon"-quality bugs left in it. VERSION 5.2.2.0 - 8/5/2001 Fixed replace_text() in reusables_c.cpp so it would not throw an access violation whenever it was called with multiple targets (as in html_canonicalize()). This bug was introduced in version 5.1.0.0. Made minor revisions to the documentation for SMTPMail. VERSION 5.2.1.0 - 7/31/2001 Added a copyright block to the top of all the source files and packaged LAFcommon for distribution as free software! Changed sql_canonicalize()'s second parameter to default to NULL so it will not break backwards-compatibility. Added a second parameter to html_canonicalize() and javascript_canonicalize() so they too can return the length of the returned string. The parameters are NULL by default. Added third parameters to all versions of GetStringFromBSTR() and GetStringFromVariant() so they can return the length of the returned string. The parameters are NULL by default. Changed both versions of GetStringFromVariant() to call GetStringFromBSTR() internally instead of duplicating the code inside themselves. Changed CHTMLUtil::canonicalize(), CHTMLUtil::canonicalize_no_break(), CJavascriptUtil::canonicalize() and CSQLUtil::canonicalize() to remove their use of CStrings in favor of char*s. They now call the functions in reusables_c.* instead of reusables_mfc.*. Changed javascript_canonicalize(), html_canonicalize(), html_canonicalize_no_break() and sql_canonicalize() in reusables_atlcom.cpp to call their non-CString-based counterparts from reusables_c.*. This was done to improve the performance and to unify their behavior behind the common name. Changed CMisc::ByteArray2String() to use char*s instead of CStrings. Changed CPAM::authenticateUser() to use char*s instead of CStrings. Cleaned up CPAM::authenticateUser() slightly to remove useless database status checks. Added GetTimeTFromDATE() and GetDATEFromTimeT() to reusables_atlcom.* to provide the ability to convert between DATEs and time_ts since that procedure is non-obvious. Added the "Win32 Release MinDependency" build configuration. VERSION 5.2.0.0 - 7/11/2001 Added an overloaded version of GetBooleanFromVariant() to reusables_atlcom.* that accepts a parameter of type VARIANT_BOOL* instead of BOOL*. When working in COM, VARIANT_BOOL is the correct type to use, though (in this version, anyway) the compiler is kind enough to upcast to BOOL when needed. Changed the old version of GetBooleanFromVariant() to simply call the new one and correctly convert the result from VARIANT_BOOL to BOOL. Added convert_short_guid_to_guid() and convert_guid_to_short_guid() to reusables_guid.* to provide a way to convert between the two GUID formats. Added ConvertShortGuidToGuid() and ConvertGuidToShortGuid() to CMisc to expose convert_short_guid_to_guid() and convert_guid_to_short_guid() (respectively) through COM. VERSION 5.1.0.0 - 7/6/2001 Cleaned up CStack's constructors. Added GetObjectFromVariant() to reusables_atlcom.* to assist in extracting IUnknown*s from VARIANTs passed through COM. Moved the initialization code out of CObjectPool::CObjectPool() into a new function named CObjectPool::init() and changed CObjectPool::CObjectPool() to call CObjectPool::init(). Also moved the pool emptying code out of CObjectPool::~CObjectPool() into a new function named CObjectPool::empty() and changed CObjectPool::~CObjectPool() to call CObjectPool::empty(). These two changes were made to allow CObjectPool to be instantiated through calls to malloc()/calloc()/realloc() and released through calls to free(). Corrected a very serious problem in CMutexQueue::lock() that was changing internal data structures without holding the big queue lock. It appears as though the big queue locking code was removed by mistake when the timeout code was added in version 4.3.0.0. Changed CMutexQueue to make the spinlock delay configurable. It defaults to 100 msecs if no value is given during object construction. Changed CMutexQueue to limit the number of threads permitted to wait on the lock. If the limit is reached, additional lock attempts will fail with error_queue_too_large. If no value is given during object construction, queue size is unlimited. NOTE: This change only affects CMutexQueue::lock() -- upgrading or downgrading a lock does not impact the queue size. Changed the semantic meaning of CMutexQueue::get_queue_size() to return the number of threads currently waiting on the queue. Previously, it had indicated the number of nodes in the wait queue, which is not particularly useful to an external caller. Added CMutexQueue::get_queue_status() to allow a caller to determine if the queue is locked and if so, if it is exclusively or nonexclusively locked. This is only useful in a very limited number of situations because the queue status could change while the call to CMutexQueue::get_queue_status() is returning, but it provides more information than CMutexQueue::get_queue_size() alone can give. Cleaned up CObjectPool's constructors. Changed generate_nonsequential_id() to append "-ID" to the IDs instead of prepending it. This should speed up string-based comparisons slightly. This does not present a backwards-compatibility issue because nonsequential IDs are not stored persistantly and the new format has the same length as the old one. Changed generate_guid(), generate_short_guid() and generate_nonsequential_id() to accept a parameter that indicates if they should use malloc() or the C++ "new" operator. The default behavior is to use "new". Added an overloaded version of sql_canonicalize() that will accept a printf()-style format and a variable list of field values, canonicalize the string values, sprintf() the whole thing together and return the result. This function doesn't add efficiency so much as it adds simplicity for the caller. Rewrote a significant portion of replace_text() to be a little smarter about its string management -- using strcat() on long string is very inefficient. It was also running strstr() on the source string every time it was looking for a new target to replace, which meant running strstr() m*(n-1) extra times (where n is the number of targets and m is the number of total replacements needed). The changes should significantly improve performance. Added a new parameter to replace_text() and sql_canonicalize() to return the length of the result string so the caller does not immediately have to run strlen() on it. The parameter has a default value of NULL. VERSION 5.0.2.0 - 6/21/2001 Fixed CSMTP::CSMTPQueue::canonicalize_message() to correct a bug noticed by Lonnie Pryor -- HTML message sections were being incorrectly encoded when a character was being replaced by its hexadecimal representation and the line-wrapping sequence was forcing a line break in the middle of the encoded sequence. CSMTP::CSMTPQueue::canonicalize_message() now includes the concept of "unbreakable sequences" to prevent this from happening. VERSION 5.0.1.0 - 6/18/2001 Changed MUTEXQUEUE_SPINLOCK_DELAY_MSECS in reusables_MutexQueue.h to 100 instead of 0 -- the lock contention between threads was so fierce that time-dependent protected functions (like database accesses) were being resource-starved and were timing out because other threads were spinning so fast on the held lock. NOTE: This change only imposes a delay on the loop that waits for the lock to become available. If a thread can aquire the lock without having to spin, the delay will not be imposed. VERSION 5.0.0.0 - 6/18/2001 Changed generate_short_guid() to reverse the nibbles of the first four bytes of the GUID in order to put the least significant bits of the first four bytes first -- they are the ones that change the fastest, so textual comparisons of the new short GUID format will spot differences faster. This format change breaks backwards compatibility, but fortunately the only place short GUIDs are being saved persistantly are in the Americo reports database, which will be converted to the new format. Fixed several serious bugs in CMutexQueue::lock() and CMutexQueue::upgrade() that would access protected data members without holding the queue lock and that would not decrement the reference count when a non-exclusive lock attempt timed out. Changed CMutexQueue::~CMutexQueue() to allow the caller to hold an exclusive lock when the destructor fires. All threads attempting to gain the lock when the destructor fires will return fail_destruct_pending. VERSION 4.3.0.0 - 6/7/2001 Added timeout capability to CMutexQueue::lock() and CMutexQueue::upgrade() to allow the caller to specify the maximum amount of time to wait on a lock request. NOTE: This timeout system uses GetTickCount(), which is only as precise as the system timer. According to the documentation, the system timer in Win95 and above has a resolution of approximately 55 ms. VERSION 4.2.0.0 - 5/21/2001 Fixed an ASP syntax error in flash_email.asp. Added the ability to specify a faux sender and faux recipient in email.asp through CSMTP::process_message()'s apparently_from and apparently_to parameters. Added base64_decode() to reusables_c.* to decode "Base64" encoded strings used in MIME encodings and to transmit the username and password entered by the user in a web browser for Basic Authentication. Changed VersionInfo() in CCOMCollection, CHTMLUtil, CJavascriptUtil, CMisc, CPAM, CSendmail, CSMTPMail and CSQLUtil to accept VARIANT* parameters instead of short* parameters. ASP correctly sees short* parameters as "ByRef Integer", but it turns out to be impossible to pass a parameter in a way that does not generate a type mismatch error. This does not break backwards compatibility since there is no way VersionInfo() could be in use anywhere. Added the element types "zip", "uszip" and "canadianzip" to form_validations.js to validate ZIP code fields. Added reusables_MutexQueue.* to provide a general-purpose locking mechanism for uses that must distinguish between read-only and read/write access. CMutexQueue assumes that all non-exclusive accesses may occur simultaneously while exclusive accesses must occur in order. To this end, it implements a queue structure that keeps requests grouped as exclusive or non-exclusive and in FIFO order. While a non-exclusive lock is held, any new non-exclusive locking requests are granted immediate access. All exclusive lock requests are queued in FIFO order and block until the lock requests queued prior are granted and released. Once the queue begins to fill, any new requests are grouped by type and queued. For instance, if three non-exclusive lock requests are made, all three are granted. If two exclusive lock requests are made, followed by four non-exclusive requests, they are queued in order until the three non-exclusive locks are released, at which point the first exclusive lock is granted. When it is released, the second exclusive lock is granted. When it is released, the four non-exclusive locks are granted simultaneously. Queue size is limited only by available memory. NOTE: CMutexQueue makes it very easy for a thread to deadlock itself if it requests several locks at once. Changed CCOMCollection::CCOMCollectionSharedNode, CCache, CMisc, CPAM, CObjectPool and CSMTP to use CMutexQueue instead of CMutex. In addition to a performance win, this also cleans up some of the wierd spinlocking code that was adequate but used the "quick draw" (random) technique of lock ordering instead of "first come, first served". Fixed a problem in CSMTP::CSMTPQueue::deliver_message() that would ignore an accountability that included CSMTP::process_batch_attempt_all if max_recipients_per_message were set to 1 or if the last recipient in the script were rejected. Now the CSMTP::process_batch_attempt_all accountability will only be ignored if only one addressee is given. Added the capability to perform form field substitutions in an email's subject line to email.asp. Fixed email.asp to correctly handle incomplete form field markers. VERSION 4.1.1.0 - 4/13/2001 Changed CSMTP, CSMTPMessage, CSMTPQueue and CSMTPQueuePool to use malloc()/ free() instead of new/delete whenever possible to allow the use of realloc() for massively repetitive procedures where a lot of memory is being allocated and freed (e.g. CSMTPQueue::generate_script()). Fixed several potential buffer overruns in CSMTP, CSMTPMessage, CSMTPQueue and CSMTPQueuePool where insufficent space was being allocated to store data. Fixed several instances in CSMTP, CSMTPMessage, CSMTPQueue and CSMTPQueuePool where the memory allocation scheme was inconsistant (i.e. memory was being allocated with new but deallocated with free()). Changed CSMTP::CSMTPQueue::generate_script() to use realloc() while creating new recipient scripts to avoid repeatedly deallocating and reallocating potentially large memory areas. VERSION 4.1.0.0 - 3/27/2001 Changed CSMTP's accountability values to support bitwise ORed values to produce more combinations and achieve the desired behavior. The biggest change is to force the delivery system to attempt delivery to all recipients on a message that has many recipients, even if one or more of the addresses are rejected by the server (CSMTP::process_batch_attempt_all). Added partial accountability support to CSMTP. VERSION 4.0.0.0 - 3/22/2001 (sigh) Added a(nother) warning message to the tops of email.asp, flash_email.asp, form_validations.js and lafcommon_vbs.txt to tell others not to copy those files into their projects but to access them from the "/LAFcommon" virtual directory. Fixed syntax errors in lafcommon_vbs.txt and email.asp found by Todd Gambal. Changed CCOMCollection::put_CaseSensitive() to require that the current collection must be empty before case sensitivity can be changed. This breaks backwards-compatibility, so a major version number change is required. Changed CCOMCollection::get_NamedConstant() to accept a long* instead of an EnumCOMCollectionReturnCodes* because it is intended to translate more than just return codes. This should not break backwards-compatibility. Added complete documentation of the LAFcommon.COMCollection interface. Moved the contents of COMCollectionNode.* and COMCollectionSharedNode.* into COMCollection.*. Moved the classes CCOMCollectionNode and CCOMCollectionSharedNode inside the CCOMCollection class. VERSION 3.0.0.0 - 3/6/2001 Changed email.asp and flash_email.asp to use SMTPmail instead of Sendmail. Removed all usage of CStrings from CCOMCollection, CCOMCollectionNode and CCOMCollectionSharedNode. Defined EnumCOMCollectionReturnCodes in LAFcommon.idl to provide a set of return codes for CCOMCollection functions. Changed the parameter types of CCOMCollection::get_CaseSensitive() and CCOMCollection::put_CaseSensitive() from VARIANT to VARIANT_BOOL. Changed the parameter type of CCOMCollection::Empty() from VARIANT to EnumCOMCollectionReturnCodes. Changed the parameter type of CCOMCollection::get_EnumerateKeys() and CCOMCollection::put_EnumerateKeys() from VARIANT to VARIANT_BOOL. Changed the parameter type of CCOMCollection::get__NewEnum() from VARIANT* to IUnknown**. Added the functions get_ThrowErrors(), put_ThrowErrors(), get_ErrorMessage() and get_NamedConstant() to CCOMCollection to bring its COM interface into line with the new LAF "standard" interface. NOTE: None of the above parameter type changes should affect the use of the LAFcommon.COMCollection interface from ASP; they'll actually help by letting the ASP engine perform some of the type checking. It does break any C++ projects that use LAFcommon.COMCollection through an imported type library, hence the major version number increase. Thankfully, no such projects exist (AFAIK). Cleaned up the CCache and CCacheNode constructors. Added the member variable cache_persist_secs to CCacheNode to allow each node in the cache to have a different maximum idle time. CCache::cache_default_persist_secs will only be used if CCacheNode::cache_persist_secs is equal to 0. Added LAFcommon.COMCollection.MaxIdleSecs() to expose the new cache_persist_secs property via COM. Renamed CCache::cache_secs to CCache::cache_default_persist_secs, CCache::get_timeout() to CCache::get_default_expiration_time(), CCache::set_timeout() to CCache::set_default_expiration_time() and CCacheNode::cache_timestamp to CCacheNode::cache_expiration_time. Changed the semantic meaning of the timestamp stored in CCacheNode. The value now indicates the time at which the node will expire, not the time at which the node was created. VERSION 2.7.0.0 - 2/6/2001 Added set_return_path_address() to CSMTP to allow the caller to specify the contents of the messages' "Return-Path" header field. This will allow bounced messages to be returned to the correct address even though the originator's address was hidden using the apparently_from parameter to CSMTP::process_message(). Added the property LAFcommon.SMTPMail.ReturnPathAddress as an interface to CSMTP::set_return_path_address(). Fixed a problem in CCache::compare_timestamps() that was not sorting the CCacheNodes in the cache's heap in the correct order. Fixed a problem in CHeap::find_node_index() that was incorrectly supposing the heap was stored in-order and abandoning the search for a node when the first node was found whose value was greater than the target value. Because this often meant the target node was not found, nodes were deleted from a cache's AVL tree without being removed from the heap when CCache::remove() was called. If the node's memory space was then freed, any reference to the node through the heap would result in an access violation. This was most likely the cause of the Metabase crashes observed in production. VERSION 2.6.0.0 - 1/24/2000 Added the capability for generating "multipart/related" messages to CSMTP. This means that a message can be sent out that displays its attachments, without requiring the mail client to hit a remote server to display images, for instance. Unfortunately, Microsoft Outlook is not RFC 2387- compliant, so messages sent this way will not work correctly in that environment. This is more completely documented in doc_SMTPMail.txt. Added the section types plain_text_inline, html_text_inline, text_file_inline, html_file_inline, binary_file_inline, gif_file_inline and jpeg_file_inline to CSMTP::section_types to facilitate the "multipart/related" message capability. Added CSMTP::constant2value() and CSMTPMail::Constant2Value() to allow a caller to translate a defined constant like CSMTP::success into its value if access to the constants is not possible for some reason. VERSION 2.5.3.0 -- 1/12/2001 Changed CMisc::SpawnProcessAsUser() to accept variants of type VT_EMPTY in place of missing optional parameters to achieve the same effect as leaving them out. Changed enum CSMTP::accountability_codes to cover all possible uses/needs. Since accountability hasn't been implemented yet, this shouldn't break any backwards compatibility. Changed CSMTP to use short GUIDs for all of its IDs. Changed CSMTPMail::ProcessMessage() to accept VARIANTs of type VT_EMPTY in place of missing optional parameters to achieve the same effect as leaving them out. Added complete documentation of LAFcommon.SMTPMail in the file doc_SMTPMail.txt. VERSION 2.5.2.0 -- 1/2/2001 Moved the class declarations and definitions of CAVLTreeNode, CCacheNode, CObjectPoolNode and CQueueNode from their reusables_*Node.* files into their respective reusables_*.* files. For example, reusables_AVLTreeNode.h and reusables_AVLTreeNode.cpp have been merged into reusables_AVLTree.h and reusables_AVLTreeNode.h. This should make including these classes in other projects a simpler exercise. Changed the behavior of the "select" element type in form_validations.js -- the value of the selected option must not be zero-length if the element is required. Removed the horribly out-of-date changelog entries from form_validations.js and replaced them with usage instructions. VERSION 2.5.1.0 -- 12/6/2000 Fixed CMisc::SpawnProcessAsUser() to open the thread token with TOKEN_DUPLICATE access so CreateProcessAsUser() will succeed when running as a user other than SYSTEM. Discovered that the user calling CMisc::SpawnProcessAsUser() must have "Act as part of the operating system", "Increase quotas" and "Replace a process level token" permissions in NT User Manager. When this method is invoked from an ASP application running "in a separate memory space", it runs as IWAM_machinename who, by default, has none of these permissions. If an ASP application is running in-process, it runs as SYSTEM, which implicitly has all permissions. Modified CMisc::SpawnProcessAsUser() to return the value from GetLastError() in the process_info parameter if LAFCOMMON_MISC_error_cannot_spawn, LAFCOMMON_MISC_error_cannot_change_user_context or LAFCOMMON_MISC_error_cannot_login are being returned. This should aid debugging in the future. VERSION 2.5.0.0 -- 12/4/2000 Added CSMTP::text_file_attachment, CSMTP::html_file_attachment, CSMTP::gif_file_attachment, CSMTP::jpeg_file_attachment and CSMTP::binary_file_attachment as section types to read files from disk and attach them to the message. If one of these section types are given, the filename is expected as the content. Fixed a bug in CSMTPQueue::canonicalize_message() that was occassionally appending garbage characters to the end of the canonicalized text. Renamed some of the preprocessor identifiers in reusables_SMTP.h so they wouldn't conflict with existing code when the file is included in another project. VERSION 2.4.0.0 -- 11/21/2000 Added CMisc::SpawnProcessAsUser() to allow callers to start external processes as a specific user. Because this is a potential avenue for brute-force password attacks, all calls through this function are serialized and given an unconditional 10 second delay (similar to CPAM::authenticateUser(). NOTE: This function will always fail with return code LAFCOMMON_MISC_error_cannot_login if the user does not have "Log on as a batch job" permission on the server. VERSION 2.3.1.0 -- 11/10/2000 Changed CSMTP::CSMTPQueue::canonicalize_message() so that it would not wrap plain text message lines at 72 characters -- the correct length is 996. VERSION 2.3.0.0 -- 11/9/2000 Added generate_nonsequential_id() to reusables_guid.* that generates between 3 and 3000 GUIDs, selects two of them at random and XORs the first eight characters of one with the last eight characters of the other. The MD5 hash of the result is then calculated and returned. Additionally, the hash is encoded in base 32 using an alphabet of "ABCDEFGHIJKMNPQRSTUVWXYZ23456789" and preceded by "ID-", which reduces the string length to 29 characters. NOTE: This function is intended to be used to generate nonsequential identifiers so that the next/previous elements in the series cannot be predicted. However, by XORing the two GUIDs, the property of being globally unique is destroyed. The identifiers returned by this function _should_ be unique within a single running instance of a given program on a given machine. A second instance of the same program, even on the same machine, may generate duplicate identifiers. NOTE: Because of the extra operations, this function is at least 100 times slower than generate_guid(). Added the COM function NonSequentialIDGen() to the interface LAFcommon.Misc that returns a result from generate_nonsequential_id(). Added generate_short_guid() to reusables_guid.* that generates a GUID and encodes it in base 32 using an alphabet of "ABCDEFGHIJKMNPQRSTUVWXYZ23456789", reducing the string length to 26 characters. Added the COM function ShortGuidGen() to the interface LAFcommon.Misc that returns the result from generate_short_guid(). VERSION 2.2.3.0 -- 11/7/2000 Rewrote a good portion of CSMTP::CSMTPQueue::canonicalize_message so it would stop eating text and begin actually working. VERSION 2.2.2.0 -- 11/6/2000 Removed a pair of unneccessary LAFcommon-specific #include directives from reusables_SMTP.h and reusables_SMTP.cpp that prevented those files from being included in other projects. Corrected a bug in CSMTP::capture_queue() that made reference to CCache::success to use CAVLTree::success instead. This was only a semantic bug since the two values are equal. Fixed a bug in CSMTP::CSMTPQueue::canonicalize_message() that was causing a buffer overrun in plain text messages. Added a call to WSAStartup() to CSMTP::CSMTP() and a call to WSACleanup() to CSMTP::~CSMTP() to facilitate brain-dead run time environments like Windows Scripting Host that won't initialize the windows sockets subsystem on their own. VERSION 2.2.1.0 - 10/26/2000 Fixed CSMTPMail::CSMTPMail() to set throw_errors to VARIANT_FALSE. Added a default condition to the switch in CSMTPMail::return_code_to_hresult(). Changed LAFcommon.SMTPMail's interface so that AddSection(), QueueFlush, QueuePause(), ProcessMessage() and WaitUntilEmpty() now return return codes instead of communicating exclusively through ErrorMessage. Removed the "Win32 Release MinDependency" configuration from the list of available build configurations. VERSION 2.2.0.0 - 10/25/2000 Created CSMTP as a complete replacement for CSendmail (and LAFcommon.SMTPMail as a complete replacement for LAFcommon.Sendmail). It supports named queueing similar to what CCOMCollection provides through its "SharedIndex" property, full MIME 1.0 encoding support and a much cleaner API. Additionally, it is much more polite to the mail server (sends termination signals and waits for acknowlegment before dropping the connection) and has been written as a reusable file to allow its inclusion in other projects without forcing those projects to call LAFcommon through COM. As a result of this, use of CSendmail is now deprecated. Removed CMailMessage and CMailQueue and their files MailMessage.* and MailQueue.*. Removed global_mailqueue() from globals.*. Removed all logic from CSendmail and hooked it in as an interface to CSMTP. Fixed a serious bug in replace_text() that would cause a buffer overrun if the shortest target was longer than the longest replacement. Added GetErrorFromVariant() to reusables_atlcom.* to make optional parameters possible in COM interfaces -- optional parameters that are not supplied are visible as VARIANTs of type VT_ERROR. Fixed a potential problem in continually_expire_shared_collections() where the start time was declared as a static variable. If the worker thread were ever stopped, it would never be able to restart because the start time would not update. Added CQueue::count() to return the number of nodes currently in the queue. Added direct access member functions to CQueue that deal with CQueueNode* arguments and return CQueueNode* data instead of void* data to make deriving classes from CQueue and CQueueNode more useful. All of the direct access functions are named after their standard equivalents but are preceeded by "internal_". Fixed a pair of bugs in CQueue::remove_current_move_prev() and CQueue::remove_current_move_next() that were returning the wrong local variable as a void*, bypassing the compiler's type checking. Modified CMisc::GuidGen() to use generate_guid() in reusables_guid.*. Added the files reusables_guid.h and reusables_guid.cpp to house the GUID generation functions instead of CMisc. Now projects can include those files directly instead of being forced to call LAFcommon via COM. This deserved its own file instead of being added to reusables_c.* because the RPC functions require linking against rpcrt4.lib, which may not be desirable if the project isn't going to use GUIDs. Fixed a potentially lethal bug in CObjectPool::~CObjectPool() that assumed CObjectPool::delete_function was valid while destroying the contents of the pool. Added GetBSTRFromString() to allow the creation of BSTRs from char* data without the use of CStrings. Removed CPAMprivate and moved all of its functionality into CPAM. The original need for CPAMprivate was to accomplish the serialization of authentication requests, which can be done more efficiently within CPAM. Changed CPAM to use EnumPAMReturnCodes to make its return codes available through the typelib. Because VBScript can't read typelibs, matching constants are also in LAFcommon_vbs.txt. Fixed a potential problem in CObjectPool::~CObjectPool() that would assert if CObjectPool::delete_function was NULL. VERSION 2.1.0.0 - 10/3/2000 Changed CSendmail::internal_sendmessage() to return LAFCOMMON_return_delivery_failure if the remote server could be contacted but the message was refused some other reason. Moved all of the version information into version.h, then included that file from resource.h and LAFcommon.idl to make updating version numbers easier. Moved the enumerated values CSendmail::delivery_modes, CSendmail::return_codes into LAFcommon.idl as EnumDeliveryModes and EnumReturnCodes, respectively, and updated all references in the code to reflect this. Added the constants from EnumDeliveryModes and EnumReturnCodes to lafcommon_vbs.txt for use in ASP. Hopefully this will reduce (and eliminate) the use of COM properties to provide constant values. Added VersionInfo() to CCOMCollection, CHTMLUtil, CJavascriptUtil, CMisc, CPAM, CSendmail and CSQLUtil to provide a way to discover the version information of the installed library without resorting to Win32 calls or physically going to the server and pulling up the properties of the DLL. VERSION 2.0.4.0 - 9/26/2000 Fixed a stupid programmer error in CMisc::Sleep that was causing the function to call itself instead of the global Sleep() function. This _should_ have been causing stack overflow errors... VERSION 2.0.3.0 - 9/22/2000 Fixed a memory leak in CSendmail::internal_sendmail(). VERSION 2.0.2.0 - 9/20/2000 Added CMisc::Sleep() to provide a way for VBScript programs to pause for a specified interval. This is especially useful when looping on CSendmail's asynchronous queue to allow it to empty before exiting. VERSION 2.0.1.0 - 8/15/2000 Changed GetStringFromVariant() and GetStringFromBSTR() to use WideCharToMultiByte() instead of W2A() to avoid copying the string twice. Since W2A() calls WideCharToMultiByte() after declaring stack space and measuring string length and most of the LAF C++ COM components use GetStringFromVariant() at least once per method, this should be a big performance win. Overloaded GetStringFromVariant() and GetStringFromBSTR() to accept char**s in addition to CString*s. These functions will allow string extraction directly to char*s without the overhead of the CString class. Changed CCOMCollection::put_Item() so that assigning a VARIANT marked VT_EMPTY in a collection item will delete that item. Added CAVLTree::traverse_preorder() and CAVLTree::traverse_postorder() mostly for completeness' sake but also to aid in diagnostic testing of the AVL tree implementation -- printing out two different traversals of the tree is easier to deal with than walking the entire structure through the QuickWatch window in DevStudio. Added the COM enumeration EnumTraversalOrders to be used as the source of the values stored in LAFcommon.COMCollection.TraversalOrder. As it turns out, VBScript is too brain-damaged to use COM enumerations, even though VB understands them. FTSOBG. Added lafcommon_vbs.txt to provide the constant values contained in EnumTraversalOrders so that VBScript can access them. Added the COM property LAFcommon.COMCollection.TraverseOrder to allow the caller to set the desired traversal order prior to getting the enumeration of the stored data. This property's value defaults to inorder traversals. Fixed a heap-corrupting bug in CCOMCollectionSharedNode::case_insensitive_find_function -- CString::Compare was being called instead of CString::CompareNoCase. Because CCOMCollectionSharedNode::case_insensitive_find_function was actually comparing in a case-sensitive manner while CCOMCollectionSharedNode::case_insensitive_comparison_function was not, the cache of shared collections was becoming corrupt when the collections expired and were removed. This was the cause of the observed assertions, crashes and hangs in LAFcommon.CCOMCollection. Changed CAVLTree::~CAVLTree() to delete all of the tree nodes when the tree is destroyed. This _shouldn't_ cause any problems unless something is using the tree nodes in another data structure. In any case, it plugs a potential memory leak. VERSION 2.0.0.0 - 8/9/2000 Finally discovered how to accept function pointers through a COM interface, run the referenced function and grab the output. As it turns out, VariantChangeType() does it all. GetStringFromBSTR(), GetEmptyFromVariant(), GetBSTRFromVariant(), GetStringFromVariant(), GetNumberFromVariant() and GetBooleanFromVariant() will all now run VariantChangeType() to convert the given VARIANT to the expected type if it wasn't of that type already. GetNumberTypeFromVariant(), GetArrayFromVariant() and GetArrayTypeFromVariant() will also use VariantChangeType() if an additional parameter is passed to indicate what type is desired. Because VariantChangeType() cannot convert arrays from one type to another, no assumptions can be made about the the desired type. The biggest benefit of this change is that ASP can now contain statements such as "Foo.Bar(Request.Form("baz"))" instead of "Foo.Bar(CStr(Request.Form("baz")))". Fixed a bug that was corrupting memory and throwing exceptions in CAVLTree::SLOW_EXPENSIVE_LINEAR_NONINDEXED_subfind() by performing recursive actions on NULL subtrees. Fixed CSendmail::internal_sendmail() to check for the value INVALID_SOCKET to see if the call to socket() succeeded. Added CQueue::delete_function to provide the ability to delete the data stored in the queue when the queue is destroyed. Added flash_email.asp to provide Flash movies the capability to send email. VERSION 1.9.10.0 - 7/28/2000 Added "creditcard", "visa", "mastercard", "americanexpress", "discover", "dinersclub" and "jcb" as valid form element types in form_validations.js to facilitate credit card processing. Added GetStringFromBSTR() to reusables_atlcom.cpp as a way to convert BSTRs to CStrings. Finally changed CAVLTree::SLOW_EXPENSIVE_LINEAR_NONINDEXED_find() and CCache::SLOW_EXPENSIVE_LINEAR_NONINDEXED_hit() to return all of the matches, not just the first one found. VERSION 1.9.9.0 - 7/24/2000 Added GetEmptyFromVariant() as an analog to the Get*FromVariant() family of functions to verify that a variant actually contains VT_EMPTY instead of inferring it when no other datatype can be extracted from it. Added CAutoArray::init() as an explicit initializer to allow CAutoArray objects to be constructed using _alloca(). Added CAutoArray::empty() and CAutoArray::delete_function to allow the destructor to delete the objects stored in the array. VERSION 1.9.8.0 - 7/18/2000 Fixed GetStringFromVariant() to use W2CA() instead of wcstombs(). Apparently wcstombs() cannot deal with Unicode characters that cannot be mapped to a value on the ANSI character map and stops. W2CA() converts the unmappable Unicode character into two bytes anyway and continues, which is what is needed. FTSOBG. Fixed a potentially fatal bug in CStack::empty() that would attempt to free the stack's array even if there were no elements in the array. Converted CAutoArray, CHeap and CStack to use realloc() to extend their arrays instead of unconditionally reallocating the memory and freeing the old array. This should provide a performance win as well as helping to prevent heap fragmentation on long-running processes. VERSION 1.9.7.0 - 7/17/2000 Added find_function and compare_void_ptrs() to CHeap. Fixed a major data corruption issue in CHeap and (by extension) CCache. When CHeap::remove() was called, it was using comparison_function to perform a linear search of the heap array for the target element. This technique is low cost because the most common caller of remove() is pop(), whose target element is always the first element in the array. However, the target element was never being found with comparison_function if the node's comparison data had changed since the target node was initially located, as is the case with CCache::remove(). By providing a separate find_function, this situation can be avoided by providing a separate function to compare the objects in the case of removal. By default, this function is set to CHeap::compare_void_ptrs(). This bug was introduced in version 1.9.3.0 and was located by Ryan Carver. Fixed CCache::remove() to pass the correct pointer address to CHeap::remove() instead of the address of the correct address (found_node was not being dereferenced). This bug was introduced in version 1.9.3.0 and was located by Ryan Carver. VERSION 1.9.6.0 - 7/15/2000 Fixed CPAMprivate::authenticateUser() to unlock the mutex if the database connection calls throw exceptions. Fixed html_canonicalize_no_break() in reusables_mfc.cpp so that it wouldn't walk off the end of the targets and replacements arrays. Changed all functions that accepted CStrings as arguments to accept CString*s instead, to avoid the overhead of making a copy of the string for the local variable. Added overloaded versions of html_canonicalize(), html_canonicalize_no_break(), sql_canonicalize() and javascript_canonicalize() that accept CString*s as parameters. Changed CPAMprivate::authenticateUser() to open its recordset without creating a temporary stored procedure on the server. VERSION 1.9.5.0 - 7/14/2000 Fixed html_canonicalize() in reusables_mfc.cpp to not replace "<" with "<" and immediately replace "&" with "&". Changed CHTMLUtil::canonicalize() and CHTMLUtil::canonicalize_no_break() to call html_canonicalize() and html_canonicalize_no_break() (respectively) in reusables_mfc.cpp instead of reusables_c.cpp. Fixed CMisc::QuickSort() to return a SAFEARRAY of the same shape as the one it originally received instead of unconditionally setting the lower bound to 1. VERSION 1.9.4.0 - 7/10/2000 Added CMisc::QuickSort() to provide a general-purpose sorting algorithm for ASP to use on arrays of strings. Changed the first parameter of quicksort() from void*[] to void** to make it work with other array types casted to void**. Fixed quicksort() so it would sort instead of shuffle. Changed GetStringFromVariant() to use wcstombs() instead of passing through a _bstr_t object to extract the BSTR as a char*. This should be a major performance win. Added GetBSTRFromVariant() to extract the BSTR from a VARIANT without converting it to a char*. VERSION 1.9.3.0 - 7/8/2000 Fixed a bug in GetNumberTypeVariant that was leaving the return value completely uninitialized in all cases, meaning that the function almost always returned a non-zero integer. Added CQueue::empty() to provide a way to empty the queue without having to destroy the object. Changed CStack to use an array of void*s instead of a separate object for every datum. This should help performance somewhat. As a result, the class CStackNode was removed and the files reusables_StackNode.cpp and reusables_StackNode.h were deleted. Changed CHeap to use an array of void*s instead of a separate object for every datum. This should help performance somewhat, except in cases where an element in the middle of the heap is to be updated, such as when a cache element's timestamp is changed. For large caches, this may become expensive enough that keeping track of the heap indecies elsewhere (such as in CCacheNode) may become worthwhile. As a result, the class CHeapNode was removed and the files reusables_HeapNode.cpp and reusables_HeapNode.h were deleted. Fixed CCOMCollection to make copies of its data using VariantCopyInd() instead of VariantCopy(). ASP passes in most of its data in VARIANTs marked VT_BYREF, which (apparently) VariantCopy() does not copy correctly. VERSION 1.9.2.0 - 7/6/2000 Removed some cruft from CCOMCollectionNode::CCOMCollectionNode(). Added GetArrayTypeFromVariant() as a counterpart to GetArrayFromVariant() to determine the type of information stored in the array. Changed CHeap::~CHeap() to destroy all of the nodes as well as the heap array. Added CQueue and CQueueNode as a general-purpose FIFO queue with enhancements to allow accesses in the middle of the queue. Added the property COMCollection.EnumerateKeys to allow the caller to specify if the stored keys should be enumerated instead of the data associated with each key since _NewEnum can't be easily made to accept an additional parameter. Cleaned up a large amount of ASP-related cruft in CCOMCollection, CHTMLUtil, CJavascriptUtil, CMisc, CPAM, CSendmail and CSQLUtil. Specifically, the onStartPage and onEndPage functions were removed, along with all of the pointers to the ASP objects. VERSION 1.9.1.0 - 7/1/2000 Fixed CCOMCollection::put_CaseSensitive() and CCOMCollection::get_CaseSensitive() to use the current (shared) collection instead of always using the internal (non-shared) collection. Removed CCOMCollection::EmptyAll() because it was simply too dangerous -- the potential for misuse was way too high. VERSION 1.9.0.0 - 6/30/2000 Fixed a problem with new CMisc::ByteArray2String() to return the entire byte array rather than the first nibble of each byte. Removed the references to resource.h and stdafx.h in reusables_AutoArray.cpp, reusables_AVLTree.cpp, reusables_AVLTreeNode.cpp, reusables_Cache.cpp, reusables_CacheNode.cpp, reusables_Heap.cpp, reusables_HeapNode.cpp, reusables_ObjectPool.cpp, reusables_ObjectPoolNode.cpp, reusables_Stack.cpp and reusables_StackNode.cpp to make it possible to use those files in non-MFC projects. Corrected a bug in CAVLTree::absorb() that was causing the absorbing tree to always completely rebuild the absorbed tree even if the absorbing tree was already empty. Along the way, the absorbing tree was resetting its internal element counter to zero, causing CAVLTree::traverse_inorder() to malfunction. Added CAVLTree::get_num_elements() to provide access to the number of elements in the tree, since the class keeps track of that information anyway. Added GetNumberTypeFromVariant() as a counterpart to GetNumberFromVariant() to determine what type of number is contained by the variant, since GetNumberFromVariant() converts the number regardless of its original type. Overloaded CAVLTree::traverse_inorder() to allow each element, as it is located, to generate a callback to a function pointer instead of unconditionally allocating (huge amounts of) memory on the heap to duplicate the tree's contents. Overloaded CCache::hit(), CCache::add() and CCache::remove() to deal with CCacheNode pointers directly instead of void pointers. This allows the ability to subclass CCacheNode and not have CCache automatically wrap it in another CCacheNode. Changed CCache::expire_entries() and CCache::remove() to allow the delete_function to be NULL. Added LAFcommon.COMCollection as a substitute for the general-purpose collection engine provided by ASP's session variables. LAFcommon.COMCollection also provides the ability to share a collection across thread boundaries by setting the COMCollection.SharedIndex property to a non-Empty value. VERSION 1.8.0.0 - 6/21/2000 Fixed CObjectPool::get_object() not to cause a memory violation if all of the objects in the pool were expired for any reason. Fixed a memory leak in CStack::empty() that would manifest if CStack::delete_function was non-NULL. Added CMisc::ByteArray2String() to convert a VARIANT-encapsulated SAFEARRAY of BYTE data to a string representation of the BYTE hex values. This is specifically for use by the ticketing system to convert ADSI user SIDs to strings. VERSION 1.7.6.0 - 6/13/2000 Fixed a small thinko in CAVLTree::remove() that was corrupting the tree by not recalculating the nodes' heights properly after a removal. VERSION 1.7.5.0 - 6/9/2000 Fixed a logic error in CAVLTree::remove() that was corrupting the tree by losing track of the target node's replacement whenever the target node had a height of 1. Fixed a nasty bug in CAVLTree::remove(). CAVLTree::rebalance() was being passed a pointer to a local variable instead of a pointer to the pointer within the tree that stored the node's location. Thus, any changes below the node were saved but changes to the node's position in the tree were not. Result: Massive data corruption. VERSION 1.7.4.0 - 5/11/2000 Fixed CAutoArray::sort() so it would sort arrays with only two elements. Fixed quicksort() so it would actually sort by correcting its understanding of what the comparison function returns. VERSION 1.7.3.0 - 5/9/2000 Changed the date format in CSendmail::internal_sendmessage() to accomodate Netscape Communicator. Apparently, Communicator is NOT RFC-822 compliant -- it does not accept timezone names, only numeric representations of the difference between GMT and local time. VERSION 1.7.2.0 - 4/11/2000 Fixed validate_isInteger() in form_validations.js so that integers could not contain negative exponentiations. Added CHeap::is_initialized() to provide a way for external methods to tell if the heap is ready to be used. Added CObjectPool and CObjectPoolNode to provide a reusable object pool system. Though it is intended primarily for database connection pooling, it makes no assumptions about the objects it is holding -- allowing it to be used with any type of object. It allows a maximum pool size and a maximum object age to be specified. Fixed validate_isInteger() in form_validations.js so that the exponential delimiter ("e" or "E") can only be specified once. Fixed several in form_validations.js where the String.replace() function was being called but the conditional would always conclude the format was correct. VERSION 1.7.1.0 - 3/15/2000 Fixed a serious bug in CCache::empty() that was calling CAVLTree::remove with parameters of the wrong type. It now calls (*get_indexed_field)() to obtain the correct pointer to pass. This bug was throwing an exception every time the function was called. Changed the growth pattern of CHeap -- it now will grow by increment_size elements until the size of the heap is equal to or greater than increment_size * 10. After that, it will double in size every time it reallocates. This should significantly improve performance in situations where the heap is growing rapidly towards a large upper bound. Added CHTMLUtil::canonicalize_no_break() and ::html_canonicalize_no_break() to both reusables_c.* and reusables_mfc.* to canonicalize an HTML string without replacing the linebreaks with "
" tags. This is primarily for use in HTML TEXTAREAs. VERSION 1.7.0.0 - 3/13/2000 Added an asynchronous mail delivery system to CSendmail::Sendmessage(). It fires off a preset number of worker threads to deliver mail in the background. Added several properties to LAFcommon.Sendmail to facilitate this: Delivery_Mode (get, put), Max_Worker_Threads (get, put), Queue_Size (get), LAFSendmail_Deliver_Synchronous (get) and LAFSendmail_Deliver_Asynchronous (get). If the background mail delivery fails, the message(s) are quietly discarded. This should be changed to produce log messages at minimum. Also, there is currently no way to clear the queue of messages or show the contents of the queue. Moved sendmail_return_codes inside the CSendmail class where it belongs. Added the value SRCbad_parameter to sendmail_return_codes for use by CSendmail::SendMessage and added the property LAFcommon.Sendmail.LAFSendmail_Bad_Parameter for use by external clients. Changed CSendmail::is_initialized() to return false if the SMTP port or the sender's email address haven't been specified, in addition to checking the SMTP server name. VERSION 1.6.5.0 - 2/28/2000 Added quicksort() to reusables_c.cpp as a generic quicksort routine. This implementation is recursive and requires the caller to provide a pointer to a comparison function. Added CAutoArray::sort() to sort the array using quicksort(). Added a parameter to CAVTree::traverse_inorder() to allow the caller to provide a pointer to a function to clone the elements as they are traversed. This is to allow for the situation where a traversal is to be stored separately after the tree is destroyed. Fixed a potentially nasty bug in CAVLTree::empty() that could have potentially resulted in attempting to free the NULL pointer. Added CAVLTree::absorb() to merge the contents of two AVL trees into one tree. It is the caller's responsibility to ensure that the merge makes sense (both trees storing the same datatype; the find_function, delete_function and comparison_function working for the contents of both trees). VERSION 1.6.4.0 - 2/3/2000 Fixed CSendmail::Sendmessage to send a carriage return as well as a linefeed at the end of each of its lines so that mail servers that are strictly compliant with RFC 822 will work. VERSION 1.6.3.0 - 1/24/2000 Added support to email.asp to replace the sequence ###\n### with a line break. Fixed CSendmail::SendMessage to be RFC822-compliant: it now sends the correct header fields in the correct order, in addition to formatting the date properly. The resultant messages are now visible in Netscape for Mac, which is apparently rather picky about such things. Added the property LAFcommon.Sendmail.reply_to, which holds the Reply-to addresses for the new messages. If no value is given, the value of LAFcommon.Sendmail.sender_email is used. VERSION 1.6.2.0 - 1/15/2000 Fixed a pair of subtle bugs in CAVLTree::rotate_double() and CAVLTree::rebalance that were corrupting the tree. VERSION 1.6.1.0 - 12/20/1999 Added CCache::get_timeout() and CCache::set_timeout() to allow cache timeouts to be configured after the cache has been initialized. Removed the configurations "Win32 Unicode Debug", "Win32 Unicode Release MinSize" and "Win32 Unicode Release MinDependency" from the build configuration options. VERSION 1.6.0.0 - 12/17/1999 Added reusables_mfc.h and reusables_mfc.c to hold new implementations of sql_canonicalize(), html_canonicalize() and javascript_canonicalize(). These new versions work on CStrings instead of char *s. VERSION 1.5.3.0 - 12/15/1999 Added CStack::empty() and CStack::delete_function to facilitate the use of CStack outside of CAVLTree. CStack::delete_function is a callback to a function that knows how to delete the objects CStack is storing. CStack::delete_function is NULL by default; it is not called if NULL. CStack::empty() forcibly empties the stack of all its elements, passing them to CStack::delete_function if it is set. VERSION 1.5.2.0 - 12/14/1999 Corrected a bug in CCache that corrupted the AVL tree when removing items. It required the addition of a get_indexed_data function pointer to get the actual field that the AVL tree indexes. This procedure probably needs to be reexamined. VERSION 1.5.1.0 - 12/12/1999 Extended CAVLTree to allow a runtime-defined delete function. This is needed so CAVLTree::empty() can delete the data being stored by the tree as well as deallocating the tree structure itself. This is not needed when using CAVLTree though CCache, but it becomes handy if CAVLTree is being used alone. Audited CHeap and found an embarrassing number of bugs: Fixed a bug in CHeap::remove() wherein the element being moved from the bottom of the heap to the top would not have its heap_index member updated if CHeap::heap_down() did not take action. This meant that, for all practical purposes, the heap was being corrupted. Corrected a major bug in CHeap that would (should) have eventually crashed the program running code -- CHeap::add()'s reallocation of the available heap space only worked the first time it was called; any subsequent expansions only wrote off into unallocated memory. Corrected a major bug in CHeap::pop() to remove the assumption that the heap was not empty. Violating that assumption would have meant pseudo-random memory corruption within the heap or a crash, depending on the value found at CHeap::heap[0]->heap_index. Corrected CHeap::heap_down() to make the correct comparisons between nodes in the heap. It was contradicting itself in its comparison behavior (in one place behaving like a maximizing heap and in another behaving like a minimizing heap). VERSION 1.5.0.0 - 12/11/1999 Deprecated the implementations of "CString GetStringFromVariant(VARIANT *)" and "SAFEARRAY *GetArrayFromVariant(VARIANT *)" in favor of the new functions "int GetStringFromVariant(VARIANT *, CString *)" and "int GetSafeArrayFromVariant(VARIANT *, SAFEARRAY **)", respectively. Both of the new functions take a new parameter -- the location into which they are to store the result. If the data cannot be extracted from the VARIANT, the second parameter is not touched and 0 is returned. If the data can be extracted, the second parameter is changed to contain its value and 1 is returned. The deprecated versions of the functions still exist for backward compatibility, though they now call the new versions and interpret the results. VERSION 1.4.0.0 - 12/9/1999 Moved the guts of HTMLUtil.canonicalize, JavascriptUtil.canonicalize and SQLUtil.canonicalize into html_canonicalize(), javascript_canonicalize() and sql_canonicalize(), respectively. These three new functions are located in reusables_c.c. This was done so that other C++ projects could use these functions directly instead of having to invoke the overhead of passing strings through COM. Hopefully this will make string canonicalization less painful along fast paths. NO CHANGES MADE TO EXTERNAL INTERFACE. VERSION 1.3.4.0 - 12/3/1999 Changed GetBooleanFromVariant to return a failure flag instead of FALSE if a boolean could not be extracted from the VARIANT. The boolean is now returned in a pass-by-reference parameter. VERSION 1.3.3.0 - 12/1/1999 Added AVLTree.SLOW_EXPENSIVE_LINEAR_NONINDEXED_find() to find an element in the AVLTree given an arbitrary search function. This function is so named because it actually does a preorder traversal of the tree and stops when it finds a match. The worst-case complexity of this search is O(n) if the target element is the largest one in the tree. *** NOTE *** NOTE *** NOTE *** This function searches the tree in preorder and stops when it finds a single match. Because the search uses an arbitrary search function to search against arbitrary criteria, there may be multiple matches within the tree if only this function were to continue searching. Only the first match is returned. Added CCache.SLOW_EXPENSIVE_LINEAR_NONINDEXED_hit() to find an element in the cache given an arbitrary search function. This function uses AVLTree.SLOW_EXPENSIVE_LINEAR_NONINDEXED_find() to locate the element. There is no performance penalty for the heap once the element has been located. *** NOTE *** NOTE *** NOTE *** This function locks the cache as a read-only thread while it works; all updates to the cache will BLOCK while this function executes. USE THIS FUNCTION SPARINGLY OR NOT AT ALL! VERSION 1.3.2.0 - 11/11/1999 Changed Misc.GuidGen() to produce GUIDs of length 32 instead of 36. This is done by cutting out the hyphens in the GUIDs. VERSION 1.3.1.0 - 11/5/1999 Added translation of carriage returns and linefeeds to line breaks in HTMLUtil.canonicalize(). Added GetDateTimeFromVariant() to reusables_atlcom.cpp since a Variant marked VT_EMPTY will generate a COleDateTime with a status COleDateTime::valid, even though the object's actual data is invalid. VERSION 1.3.0.0 - 11/3/1999 Added JavascriptUtil to canonicalize strings for use in Javascript code. VERSION 1.2.2.0 - 10/20/1999 Reworked CCacheNode not to inherit from CAVLTreeNode and CHeapNode, since it appears that the MSVC++ compiler doesn't handle multiple inheritance correctly when there is complex pointer casting going on around stack boundaries. Added get_max_index() to CAutoArray, which returns the greatest index accessed so far. VERSION 1.2.1.0 - 10/19/1999 Moved the enumerated result types from outside the CAVLTree, CCache and CHeap classes inside them. Added GetBooleanFromVariant() to reusables_atlcom.cpp. Fixed small bug in GetArrayFromVariant(). VERSION 1.2.0.0 - 10/13/1999 Created CSendmail CSendmail.IsInitialized() will return a boolean value indicating if the initialization of MAPI was successful. CSendmail.Sendmail() will send a given message with a given subject to a given recipient. VERSION 1.1.5.0 - 10/1/1999 Added CCache.empty() to dump cache contents before the object is destroyed. VERSION 1.1.4.0 - 9/30/1999 Plugged a memory leak in both HTMLUtil.canonicalize() and in SQLUtil.canonicalize(). VERSION 1.1.3.0 - 9/29/1999 Changed the locking inside CCache to handle read-only threads versus threads that need exclusive access to the structures. VERSION 1.1.2.0 - 9/28/1999 Added CAVLTree, CAVLTreeNode, CCache, CHeap, and CHeapNode. VERSION 1.1.1.0 - 9/24/1999 Took out the replacements of linebreaks and carriage returns with "
" in HTMLUtil.canonicalize(). VERSION 1.1.0.0 - 9/23/1999 Added CCacheNode, CStackNode and CStack. Began work on CAVLTree. VERSION 1.0.2.2 - 9/22/1999 No behavioral changes -- split reusables.[h/cpp] into reusables_atlcom.[h/cpp] and reusables_c.[h/cpp] because including the ATL COM stuff into non-ATL COM projects would cause linker errors when attempting to find endthreadex() and friends. Added translation of "\n\r" into "
" to HTMLUtil.canonicalize(). VERSION 1.0.2.1 - 9/22/1999 Changed the behavior of HTMLUtil.canonicalize() and SQLUtil.canonicalize() to perform as expected -- they were both changing any unprintable characters to '?'s. Unfortunately, carriage returns, newline and tabs were not printable according to isprint(). These characters are now handled as special cases. Added spider_directories() to reusables.cpp, since I seem to be using it quite a bit. VERSION 1.0.2.0 - 9/20/1999 Changed the CPAM.authenticateUser() system to enforce a single instance of itself internally rather than relying on the caller to do this. This was needed to use the CPAM system from ASP pages. Renamed globals.h and globals.cpp to reusables.h and reusables.cpp, respectively. VERSION 1.0.1.1 - 9/15/1999 Corrected the MUTEX_LOCK_TIMEOUT to indicate time in milliseconds, not in seconds. VERSION 1.0.1.0 - 9/10/1999 Created CMisc CMisc.GuidGen() will create a 36 character representation of a GUID using the "Registry" format without the leading and trailing curly braces. Added more HTML translations to CHTMLUtil.canonicalize(). VERSION 1.0.0.1 - (INITIAL VERSION) 9/9/1999 Created CPAM CPAM.AuthenticateUser() will take a database connection string and select statement. It will open a database connection using the provided connection string and run the select statement to create a recordset. If the recordset is empty, LAFPAM_FAIL is returned. If the recordset is empty, LAFPAM_SUCCEED is returned. Additionally, if the database connection string does not work, LAFPAM_DBERROR is returned. The idea behind this method is to provide a centralized, thread-independent mechanism for authenticating usernames and passwords. A process can hold a single instance of CPAM and access it using multiple threads. CPAM will then serialize the access and introduce an unconditional delay of one second before returning the result. The idea behind this system is this: If an attacker is trying to brute-force guess an online username/password system, he's going to hit the site many thousands of times simultaneously. Typically, he's going to gauge success or failure on the response time, not on the actual return value. By using CPAM, all authentication requests are forced to be served one at a time, regardless of the number of concurrent requests. Additionally, due to the one second delay, thousands of attempts will incur a serious time penalty that most attackers are not willing to pay. Created CHTMLUtil CHTMLUtil.canonicalize() will take a string and replace all HTML-sensitive characters with their HTML-safe equivalent character sequences. NOTE: This is not the same as URL- encoding the same string. In URL-encoding, spaces are changed to pluses ('+') and any other non-alphanumeric character is changed to a percent sign ('%') and its corresponding ASCII value represented as a two-character hexadecimal number. Canonicalizing an HTML string will replace characters such as greater than ('>') with an HTML-safe representation (">"). Created CSQLUtil CSQLUtil.canonicalize() will remove single quote characters ('\'') and replace them with two single quote characters ("''") so the string can safely be used as a text field in a SQL statement.