diff --git a/sites/all/modules/memcache/.gitignore b/sites/all/modules/memcache/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1377554ebea6f98a2c748183bc5a96852af12ac2
--- /dev/null
+++ b/sites/all/modules/memcache/.gitignore
@@ -0,0 +1 @@
+*.swp
diff --git a/sites/all/modules/memcache/INSTALLATION.txt b/sites/all/modules/memcache/INSTALLATION.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6ba171c47c8e32f7002d71640f0eeda7906a3c3e
--- /dev/null
+++ b/sites/all/modules/memcache/INSTALLATION.txt
@@ -0,0 +1,99 @@
+
+For instructions on how to compile memcached on Debian Etch, see here:
+
+http://www.lullabot.com/articles/how_install_memcache_debian_etch
+
+Fedora memcache + drupal walkthrough
+
+1) Have a look at the background reading
+
+  A - http://www.danga.com/memcached/	  (cache daemon)
+
+  B - http://pecl.php.net/package/memcache  (PHP integration for memcache)
+
+  C - http://drupal.org/project/memcache    (Drupal Module to use memcache)
+
+2) Fedora RPMs are available for A and B above
+
+  http://dag.wieers.com/rpm/packages/memcached/
+  http://dag.wieers.com/rpm/packages/php-pecl-memcache/
+
+  I downloaded the SRPMs and rebuilt them.  This may require installing some
+  extra RPMs into your system - rpm will tell you.
+
+ 1003  wget http://dag.wieers.com/rpm/packages/memcached/memcached-1.2.1-4.rf.src.rpm
+ 1004  rpmbuild --rebuild memcached-1.2.1-4.rf.src.rpm
+ 1005  yum install libevent-devel
+ 1006  rpmbuild --rebuild memcached-1.2.1-4.rf.src.rpm
+
+ 1008  wget http://dag.wieers.com/rpm/packages/php-pecl-memcache/php-pecl-memcache-2.0.4-1.rf.src.rpm
+ 1009  rpmbuild --rebuild php-pecl-memcache-2.0.4-1.rf.src.rpm
+ 1010  yum install php-devel
+ 1011  rpmbuild --rebuild php-pecl-memcache-2.0.4-1.rf.src.rpm
+
+3) Installed RPMS
+
+[root@yoursite ~]# rpm -Uvh /usr/src/redhat/RPMS/x86_64/memcached-1.2.1-4.rf.x86_64.rpm /usr/src/redhat/RPMS/x86_64/php-pecl-memcache-2.0.4-1.rf.x86_64.rpm
+Preparing...                ########################################### [100%]
+   1:php-pecl-memcache      ########################################### [ 50%]
+   2:memcached              ########################################### [100%]
+[root@yoursite ~]#
+
+4) Verify configuration. Change if required.
+
+[root@yoursite ~]# cat /etc/sysconfig/memcached
+PORT="11211"
+USER="nobody"
+MAXCONN="1024"
+CACHESIZE="64"
+OPTIONS=""
+[root@yoursite ~]#
+
+5) Activate memcache
+
+[root@yoursite ~]# service memcached start
+Starting Distributed memory caching (memcached):           [  OK  ]
+[root@yoursite ~]# service httpd restart
+Stopping httpd:                                            [  OK  ]
+Starting httpd:                                            [  OK  ]
+[root@yoursite ~]#
+
+  Check phpinfo() for memcached status. Mine said
+
+      memcache
+      memcache support	enabled
+      Active persistent connections 	0
+      Revision 	$Revision$
+
+6) Optional. Install and enable 'devel' module. Configure it to show SQL
+queries being executed.  Log in, and check out / and /admin - see how many
+SQL queries are run.  In my case / took 156 queries, and /admin took 91.
+Once you've patched Drupal as described below, even enabling and disabling
+the Memcache module won't disable the use of memcache.
+
+7) Download, install, follow steps in README.txt (move include file,
+configure settings.php)
+
+    [simon@yoursite ~]$ cd /home/WHOEVER/www/SOMEWEBSITE.com/html/modules/
+    [simon@yoursite modules]$ wget -q http://ftp.osuosl.org/pub/drupal/files/projects/memcache-5.x-1.x-dev.tar.gz
+    [simon@yoursite modules]$ tar xfz memcache-5.x-1.x-dev.tar.gz
+    [simon@yoursite modules]$ cd memcache
+    [simon@yoursite memcache]$ mv memcache.inc ../../includes/
+    [simon@yoursite modules]$
+
+Apply the default configuration to the bottom of your
+DRUPAL/sites/default/settings.php file.  If you have already configured
+$cfg, you will need to add the element to the array instead of appending the
+whole $cfg. Don't forget to also include 'cache.inc' and 'memcache.inc'.  The
+example below uses the default localhost:11211 server.
+
+    $ tail -3 ../../sites/default/settings.php
+      include_once('./includes/cache.inc');
+      include_once('./sites/default/modules/memcache/memcache.inc');
+      $conf['cache_default_class'] = 'MemCacheDrupal';
+    $
+
+8) Optionally enable the memcache admin drupal module from /admin/build/modules
+(it's in the "Other" section).
+
+See README.txt for more information about configuring Drupal with memcache.
diff --git a/sites/all/modules/memcache/LICENSE.txt b/sites/all/modules/memcache/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2c095c8d3f42488e8168f9710a4ffbfc4125a159
--- /dev/null
+++ b/sites/all/modules/memcache/LICENSE.txt
@@ -0,0 +1,274 @@
+GNU GENERAL PUBLIC LICENSE
+
+              Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+                  Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users. This General Public License
+applies to most of the Free Software Foundation's software and to any other
+program whose authors commit to using it. (Some other Free Software
+Foundation software is covered by the GNU Library General Public License
+instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this service if
+you wish), that you receive source code or can get it if you want it, that you
+can change the software or use pieces of it in new free programs; and that
+you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to
+deny you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients
+to know that what they have is not the original, so that any problems
+introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We
+wish to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+           GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+               MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms
+of this General Public License. The "Program", below, refers to any such
+program or work, and a "work based on the Program" means either the
+Program or any derivative work under copyright law: that is to say, a work
+containing the Program or a portion of it, either verbatim or with
+modifications and/or translated into another language. (Hereinafter, translation
+is included without limitation in the term "modification".) Each licensee is
+addressed as "you".
+
+Activities other than copying, distribution and modification are not covered
+by this License; they are outside its scope. The act of running the Program is
+not restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made
+by running the Program). Whether that is true depends on what the Program
+does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the
+Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you
+may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it,
+thus forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you
+also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that
+you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be
+licensed as a whole at no charge to all third parties under the terms of this
+License.
+
+c) If the modified program normally reads commands interactively when run,
+you must cause it, when started running for such interactive use in the most
+ordinary way, to print or display an announcement including an appropriate
+copyright notice and a notice that there is no warranty (or else, saying that
+you provide a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this License.
+(Exception: if the Program itself is interactive but does not normally print such
+an announcement, your work based on the Program is not required to print
+an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be
+reasonably considered independent and separate works in themselves, then
+this License, and its terms, do not apply to those sections when you distribute
+them as separate works. But when you distribute the same sections as part
+of a whole which is a work based on the Program, the distribution of the
+whole must be on the terms of this License, whose permissions for other
+licensees extend to the entire whole, and thus to each and every part
+regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to
+control the distribution of derivative or collective works based on the
+Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the scope
+of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1
+and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above
+on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give
+any third party, for a charge no more than your cost of physically performing
+source distribution, a complete machine-readable copy of the corresponding
+source code, to be distributed under the terms of Sections 1 and 2 above on
+a medium customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for
+noncommercial distribution and only if you received the program in object
+code or executable form with such an offer, in accord with Subsection b
+above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source code
+means all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation and
+installation of the executable. However, as a special exception, the source
+code distributed need not include anything that is normally distributed (in
+either source or binary form) with the major components (compiler, kernel,
+and so on) of the operating system on which the executable runs, unless that
+component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to
+copy from a designated place, then offering equivalent access to copy the
+source code from the same place counts as distribution of the source code,
+even though third parties are not compelled to copy the source along with the
+object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy,
+modify, sublicense or distribute the Program is void, and will automatically
+terminate your rights under this License. However, parties who have received
+copies, or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the
+Program or its derivative works. These actions are prohibited by law if you
+do not accept this License. Therefore, by modifying or distributing the
+Program (or any work based on the Program), you indicate your acceptance
+of this License to do so, and all its terms and conditions for copying,
+distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms and
+conditions. You may not impose any further restrictions on the recipients'
+exercise of the rights granted herein. You are not responsible for enforcing
+compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution
+of the Program by all those who receive copies directly or indirectly through
+you, then the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and
+the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose
+that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original copyright
+holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In such
+case, this License incorporates the limitation as if written in the body of this
+License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will be
+similar in spirit to the present version, but may differ in detail to address new
+problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+a version number of this License which applies to it and "any later version",
+you have the option of following the terms and conditions either of that
+version or of any later version published by the Free Software Foundation. If
+the Program does not specify a version number of this License, you may
+choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software
+Foundation, write to the Free Software Foundation; we sometimes make
+exceptions for this. Our decision will be guided by the two goals of
+preserving the free status of all derivatives of our free software and of
+promoting the sharing and reuse of software generally.
+
+               NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE,
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT
+PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR
+AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR
+ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
+SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE
+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
+OR DATA BEING RENDERED INACCURATE OR LOSSES
+SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN
+IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES.
+
+          END OF TERMS AND CONDITIONS
diff --git a/sites/all/modules/memcache/README.txt b/sites/all/modules/memcache/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4efd86d3880e82e852d7062b867c6ce6a1efa4f7
--- /dev/null
+++ b/sites/all/modules/memcache/README.txt
@@ -0,0 +1,220 @@
+
+## REQUIREMENTS ##
+
+- PHP 5.1 or greater
+- Availability of a memcached daemon: http://memcached.org/
+- One of the two PECL memcache packages:
+  - http://pecl.php.net/package/memcache
+  - http://pecl.php.net/package/memcached (recommended
+
+## INSTALLATION ##
+
+These are the broad steps you need to take in order to use this software. Order
+is important.
+
+1. Install the memcached binaries on your server. See 
+
+http://www.lullabot.com/articles/how_install_memcache_debian_etch
+
+2. Install the PECL memcache extension for PHP. This must be version 2.2.1 or 
+   higher or you will experience errors.
+3. Put your site into offline mode.
+4. Download and install the memcache module.
+5. If you have previously been running the memcache module, run update.php.
+6. Start at least one instance of memcached on your server.
+7. Edit settings.php to configure the servers, clusters and bins that memcache
+   is supposed to use.
+8. Edit settings.php to include cache.inc, memcache.inc.  For example:
+     include_once('./includes/cache.inc');
+     include_once('./sites/all/modules/memcache/memcache.inc');
+9. Edit settings.php to make memcache the default caching class:
+     $conf['cache_default_class'] = 'MemCacheDrupal';
+9. Bring your site back online.
+
+For instructions on 1 and 2 above, please see the INSTALLATION.txt file that
+comes with the memcache module download.
+
+http://www.lullabot.com/files/memcache-inc.png
+
+In Drupal 7+, you no longer should set cache_inc in settings.php.  Instead, you
+will have to manually include 'cache.inc' and 'memcache.inc', then update $conf
+to tell Drupal to default to memcache for caching:
+
+  // the path to the core cache file
+  include_once('./includes/cache.inc');
+  // the path to the memcache cache file
+  include_once('./sites/all/modules/memcache/memcache.inc');
+  // make MemCacheDrupal the default cache class
+  $conf['cache_default_class'] = 'MemCacheDrupal';
+
+## SERVERS ##
+
+If you want the simple version, you can start one default memcache instance on
+your web server like this: memcached -m 24 -p 11211 -d
+If that is enough to meet your needs, there is no more configuration needed. If
+you want to utilize this module's sophisticated clustering feature and spread
+your cache over several machines, or if your cache is found on a machine other
+than your web server, read on.
+
+The available memcached servers are specified in $conf in settings.php. If
+you do not specify any servers, memcache.inc assumes that you have a
+memcached instance running on localhost:11211. If this is true, and it is
+the only memcached instance you wish to use, no further configuration is
+required.
+
+If you have more than one memcached instance running, you need to add two
+arrays to $conf; memcache_servers and memcache_bins. The arrays follow this
+pattern:
+
+'memcache_servers' => array(
+  host1:port => cluster, 
+  host2:port => cluster, 
+  hostN:port => cluster
+)
+
+'memcache_bins' => array(bin1 => cluster, bin2 => cluster, binN => cluster)
+
+The bin/cluster/server model can be described as follows:
+
+- Servers are memcached instances identified by host:port.
+
+- Bins are groups of data that get cached together and map 1:1 to the $table
+  param in cache_set(). Examples from Drupal core are cache_filter,
+  cache_menu. The default is 'cache'.
+
+- Clusters are groups of servers that act as a memory pool.
+
+- many bins can be assigned to a cluster.
+
+- The default cluster is 'default'.
+
+Here is a simple setup that has two memcached instances, both running on
+localhost. The 11212 instance belongs to the 'pages' cluster and the table
+cache_page is mapped to the 'pages' cluster. Thus everything that gets cached,
+with the exception of the page cache (cache_page), will be put into 'default',
+or the 11211 instance. The page cache will be in 11212.
+
+$conf = array(
+  ...
+  // Important to define a default cluster in both the servers
+  // and in the bins. This links them together.
+  'memcache_servers' => array('localhost:11211' => 'default',
+                              'localhost:11212' => 'pages'),
+  'memcache_bins' => array('cache' => 'default',
+                           'cache_page' => 'pages'),
+);
+
+Here is an example configuration that has two clusters, 'default' and
+'cluster2'. Five memcached instances are divided up between the two
+clusters. 'cache_filter' and 'cache_menu' bins goe to 'cluster2'. All other
+bins go to 'default'.
+
+include_once('./includes/cache.inc');
+include_once('./sites/all/modules/memcache/memcache.inc');
+$conf = array(
+  'cache_default_class' = 'MemCacheDrupal',
+  'memcache_servers' => array('localhost:11211' => 'default',
+                              'localhost:11212' => 'default',
+                              '123.45.67.890:11211' => 'default',
+                              '123.45.67.891:11211' => 'cluster2',
+                              '123.45.67.892:11211' => 'cluster2'),
+
+  'memcache_bins' => array('cache' => 'default',
+                           'cache_filter' => 'cluster2',
+                           'cache_menu' => 'cluster2'),
+);
+## PREFIXING ##
+
+If you want to have multiple Drupal installations share memcached instances,
+you need to include a unique prefix for each Drupal installation in the $conf
+array of settings.php:
+
+$conf = array(
+  ...
+  'memcache_key_prefix' => 'something_unique',
+);
+
+## SESSIONS ##
+
+Here is a sample config that uses memcache for sessions. Note you MUST have
+a session and a users server set up for memcached sessions to work.
+
+NOTE: Session.inc is not yet ported to Drupal 7.
+
+include_once('./includes/cache.inc');
+include_once('./sites/all/modules/memcache/memcache.inc');
+$conf = array(
+  'cache_default_class' = 'MemCacheDrupal',
+  'session_inc' => './sites/all/modules/memcache/memcache-session.inc',
+  'memcache_servers' => array(
+    'localhost:11211' => 'default',
+    'localhost:11212' => 'filter',
+    'localhost:11213' => 'menu',
+    'localhost:11214' => 'page',
+    'localhost:11215' => 'session',
+    'localhost:11216' => 'users',
+  ),
+  'memcache_bins' => array(
+    'cache' => 'default',
+    'cache_filter' => 'filter',
+    'cache_menu' => 'menu',
+    'cache_page' => 'page',
+    'session' => 'session',
+    'users' => 'users',
+  ),
+);
+
+
+## TROUBLESHOOTING ##
+
+PROBLEM:
+Error:
+Failed to set key: Failed to set key: cache_page-......
+
+SOLUTION:
+Upgrade your PECL library to PECL package (2.2.1) (or higher).
+
+WARNING: 
+Zlib compression at the php.ini level and Memcache conflict. 
+See http://drupal.org/node/273824
+
+## MEMCACHE ADMIN ##
+
+A module offering a UI for memcache is included. It provides stats, a
+way to clear the cache, and an interface to organize servers/bins/clusters.
+
+
+## Memcached PECL Extension Support
+
+We also now support the Memcached PECL extension. If you install this extension,
+it will be used by default. This new extension backends to libmemcached and 
+allows you to use some of the newer advanced features in memcached 1.4. 
+
+NOTE: It is important to realize that the memcache php.ini options do not impact
+the memcached extension, this new extension doesn't read in options that way.
+Instead, it takes options directly from Drupal. Because of this, you must
+configure memcached in settings.php. Please look here for possible options:
+
+http://us2.php.net/manual/en/memcached.constants.php
+
+An example configuration block is below, this block also illustrates our
+default options. These will be set unless overridden in settings.php.
+
+$conf['memcache_options'] = array(
+  Memcached::OPT_COMPRESSION => FALSE,
+  Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
+);
+
+These are as follows:
+
+ * Turn off compression, as this takes more CPU cycles than its worth for most
+   users
+ * Turn on consistent distribution, which allows you to add/remove servers
+   easily
+
+If you are using memcached 1.4 or above, you should enable the binary protocol,
+which is more advanced and faster, by adding the following to settings.php:
+
+$conf['memcache_options'] = array(
+  Memcached::OPT_BINARY_PROTOCOL => TRUE,
+);
diff --git a/sites/all/modules/memcache/dmemcache.inc b/sites/all/modules/memcache/dmemcache.inc
new file mode 100644
index 0000000000000000000000000000000000000000..df3c85dc48890962876ff2d03c26889ac1a0a549
--- /dev/null
+++ b/sites/all/modules/memcache/dmemcache.inc
@@ -0,0 +1,347 @@
+<?php
+
+/*
+ * Core dmemcache functions required by:
+ *   memcache.inc
+ *   memcache.db.inc
+ *   session-memcache.inc
+ *   session-memcache.db.inc
+ */
+
+global $_memcache_statistics;
+$_memcache_statistics = array();
+
+/*
+ * A memcache API for Drupal.
+ */
+
+/**
+ *  Place an item into memcache
+ *
+ * @param $key The string with which you will retrieve this item later.
+ * @param $value The item to be stored.
+ * @param $exp Parameter expire is expiration time in seconds. If it's 0, the
+ *   item never expires (but memcached server doesn't guarantee this item to be
+ *   stored all the time, it could be deleted from the cache to make place for
+ *   other items).
+ * @param $bin The name of the Drupal subsystem that is making this call.
+ *   Examples could be 'cache', 'alias', 'taxonomy term' etc. It is possible to
+ *   map different $bin values to different memcache servers.
+ * @param $mc Optionally pass in the memcache object.  Normally this value is
+ *   determined automatically based on the bin the object is being stored to.
+ *
+ * @return bool
+ */
+function dmemcache_set($key, $value, $exp = 0, $bin = 'cache', $mc = NULL) {
+  global $_memcache_statistics;
+  $full_key = dmemcache_key($key, $bin);
+  $_memcache_statistics[] = array('set', $bin, $full_key, '');
+  if ($mc || ($mc = dmemcache_object($bin))) {
+    if (class_exists('Memcached')) {
+      return $mc->set($full_key, $value, $exp);
+    }
+    else {
+      return $mc->set($full_key, $value, MEMCACHE_COMPRESSED, $exp);
+    }
+  }
+  return FALSE;
+}
+
+/**
+ *  Add an item into memcache
+ *
+ * @param $key The string with which you will retrieve this item later.
+ * @param $value The item to be stored.
+ * @param $exp Parameter expire is expiration time in seconds. If it's 0, the
+ *   item never expires (but memcached server doesn't guarantee this item to be
+ *   stored all the time, it could be deleted from the cache to make place for
+ *   other items).
+ * @param $bin The name of the Drupal subsystem that is making this call.
+ *   Examples could be 'cache', 'alias', 'taxonomy term' etc. It is possible
+ *   to map different $bin values to different memcache servers.
+ * @param $mc Optionally pass in the memcache object.  Normally this value is
+ *   determined automatically based on the bin the object is being stored to.
+ * @param $flag If using the older memcache PECL extension as opposed to the
+ *   newer memcached PECL extension, the MEMCACHE_COMPRESSED flag can be set
+ *   to use zlib to store a compressed copy of the item.  This flag option is
+ *   completely ignored when using the newer memcached PECL extension.
+ *
+ * @return bool
+ */
+function dmemcache_add($key, $value, $exp = 0, $bin = 'cache', $mc = NULL, $flag = FALSE) {
+  global $_memcache_statistics;
+  $full_key = dmemcache_key($key, $bin);
+  $_memcache_statistics[] = array('add', $bin, $full_key, '');
+  if ($mc || ($mc = dmemcache_object($bin))) {
+    if (class_exists('Memcached')) {
+      return $mc->add($full_key, $value, $exp);
+    }
+    else {
+      return $mc->add($full_key, $value, $flag, $exp);
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Retrieve a value from the cache.
+ *
+ * @param $key The key with which the item was stored.
+ * @param $bin The bin in which the item was stored.
+ *
+ * @return The item which was originally saved or FALSE
+ */
+function dmemcache_get($key, $bin = 'cache', $mc = NULL) {
+  global $_memcache_statistics;
+  $full_key = dmemcache_key($key, $bin);
+  $statistics = array('get', $bin, $full_key);
+  $success = '0';
+  if ($mc || ($mc = dmemcache_object($bin))) {
+    $result = $mc->get($full_key);
+    if ($result) {
+      $success = '1';
+    }
+  }
+  $statistics[] = $success;
+  $_memcache_statistics[] = $statistics;
+  return $result;
+}
+
+/**
+ * Retrieve multiple values from the cache.
+ *
+ * @param $keys The keys with which the items were stored.
+ * @param $bin The bin in which the item was stored.
+ *
+ * @return The item which was originally saved or FALSE
+ */
+function dmemcache_get_multi($keys, $bin = 'cache', $mc = NULL) {
+  global $_memcache_statistics;
+  $full_keys = array();
+  $statistics = array();
+  foreach ($keys as $key => $cid) {
+    $full_key = dmemcache_key($cid, $bin);
+    $statistics[$full_key] = array('getMulti', $bin, $full_key);
+    $full_keys[] = $full_key;
+  }
+  $results = array();
+  if ($mc || ($mc = dmemcache_object($bin))) {
+    if (class_exists('Memcached')) {
+      $results = $mc->getMulti($full_keys);
+    }
+    else {
+      $results = $mc->get($full_keys);
+    }
+  }
+  foreach ($statistics as $key => $values) {
+    $values[] = isset($results[$key]) ? '1': '0';
+    $_memcache_statistics[] = $values;
+  }
+  return $results;
+}
+
+/**
+ * Deletes an item from the cache.
+ *
+ * @param $key The key with which the item was stored.
+ * @param $bin The bin in which the item was stored.
+ *
+ * @return Returns TRUE on success or FALSE on failure.
+ */
+function dmemcache_delete($key, $bin = 'cache', $mc = NULL) {
+  global $_memcache_statistics;
+  $full_key = dmemcache_key($key, $bin);
+  $_memcache_statistics[] = array('delete', $bin, $full_key, '');
+  if ($mc || ($mc = dmemcache_object($bin))) {
+    return $mc->delete($full_key);
+  }
+  return FALSE;
+}
+
+/**
+ * Immediately invalidates all existing items. dmemcache_flush doesn't actually free any
+ * resources, it only marks all the items as expired, so occupied memory will be overwritten by
+ * new items.
+ *
+ * @param $bin The bin to flush. Note that this will flush all bins mapped to the same server
+ *   as $bin. There is no way at this time to empty just one bin.
+ *
+ * @return Returns TRUE on success or FALSE on failure.
+ */
+function dmemcache_flush($bin = 'cache', $mc = NULL) {
+  global $_memcache_statistics;
+  $_memcache_statistics[] = array('flush', $bin, '', '');
+  if ($mc || ($mc = dmemcache_object($bin))) {
+    return memcache_flush($mc);
+  }
+}
+
+function dmemcache_stats($bin = 'cache', $type = '') {
+  // resolve requests for 'default' type to ''
+  if ($type == 'default') {
+    $type = '';
+  }
+  // resolve requests for 'default' bin to 'cache'.
+  if ($bin == 'default') {
+    $bin = 'cache';
+  }
+  if ($mc = dmemcache_object($bin)) {
+    if (class_exists('Memcached')) {
+      return $mc->getStats();
+    }
+    // The PHP Memcache extension 3.x version throws an error if the stats
+    // type is NULL or not in {reset, malloc, slabs, cachedump, items, sizes}.
+    // If $type is 'default', then no parameter should be passed to the
+    // Memcache memcache_get_extended_stats() function.
+    if ($type == 'default' || $type == '') {
+      if (class_exists('Memcached')) {
+        return $mc->getStats();
+      }
+      else if (class_exists('Memcache')) {
+        return $mc->getExtendedStats();
+      }
+    }
+    else {
+      if (class_exists('Memcached')) {
+        return $mc->getStats();
+      }
+      else if (class_exists('Memcache')) {
+        return $mc->getExtendedStats($type);
+      }
+    }
+  }
+}
+
+/**
+ * Returns an Memcache object based on the bin requested. Note that there is
+ * nothing preventing developers from calling this function directly to get the
+ * Memcache object. Do this if you need functionality not provided by this API
+ * or if you need to use legacy code. Otherwise, use the dmemcache (get, set,
+ * delete, flush) API functions provided here.
+ *
+ * @param $bin The bin which is to be used.
+ *
+ * @param $flush Rebuild the bin/server/cache mapping.
+ *
+ * @return an Memcache object or FALSE.
+ */
+function dmemcache_object($bin = NULL, $flush = FALSE) {
+  static $memcacheCache = array(), $memcache_servers, $memcache_bins;
+
+  if ($flush) {
+    foreach ($memcacheCache as $cluster) {
+      memcache_close($cluster);
+    }
+    $memcacheCache = array();
+  }
+
+  if (empty($memcacheCache) || empty($memcacheCache[$bin])) {
+    // $memcache_servers and $memcache_bins originate from settings.php.
+    // $memcache_servers_custom and $memcache_bins_custom get set by
+    // memcache.module. They are then merged into $memcache_servers and
+    // $memcache_bins, which are statically cached for performance.
+    if (empty($memcache_servers)) {
+      // Values from settings.php
+      $memcache_servers = variable_get('memcache_servers', array('127.0.0.1:11211' => 'default'));
+      $memcache_bins    = variable_get('memcache_bins', array('cache' => 'default'));
+    }
+
+    // If there is no cluster for this bin in $memcache_bins, cluster is 'default'.
+    $cluster = empty($memcache_bins[$bin]) ? 'default' : $memcache_bins[$bin];
+
+    // If this bin isn't in our $memcache_bins configuration array, and the
+    // 'default' cluster is already initialized, map the bin to 'cache' because
+    // we always map the 'cache' bin to the 'default' cluster.
+    if (empty($memcache_bins[$bin]) && !empty($memcacheCache['cache'])) {
+      $memcacheCache[$bin] = &$memcacheCache['cache'];
+    }
+    else {
+      // Create a new Memcache object. Each cluster gets its own Memcache object.
+      if (class_exists('Memcached')) {
+        $memcache = new Memcached;
+        $default_opts = array(
+          Memcached::OPT_COMPRESSION => FALSE,
+          Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
+        );
+        foreach ($default_opts as $key => $value) {
+          $memcache->setOption($key, $value);
+        }
+        $memconf = variable_get('memcache_options', array());
+        foreach ($memconf as $key => $value) {
+          $memcache->setOption($key, $value);
+        }
+      }
+      else if (class_exists('Memcache')) {
+        $memcache = new Memcache;
+      }
+      else {
+        drupal_set_message(t('You must enable the PECL memcached or memcache extension to use memcache.inc.'), 'error');
+        return;
+      }
+      // A variable to track whether we've connected to the first server.
+      $init = FALSE;
+
+      // Link all the servers to this cluster.
+      foreach ($memcache_servers as $s => $c) {
+        if ($c == $cluster) {
+          list($host, $port) = explode(':', $s);
+
+          // This is a server that belongs to this cluster.
+          if (!class_exists('Memcached') && !$init) {
+            // If using PECL memcache extension, use ->connect for first server
+            if ($memcache->connect($host, $port)) {
+              $init = TRUE;
+            }
+          }
+          else {
+            if ($memcache->addServer($host, $port) && !$init) {
+              $init = TRUE;
+            }
+          }
+        }
+      }
+
+      if ($init) {
+        // Map the current bin with the new Memcache object.
+        $memcacheCache[$bin] = $memcache;
+
+        // Now that all the servers have been mapped to this cluster, look for
+        // other bins that belong to the cluster and map them too.
+        foreach ($memcache_bins as $b => $c) {
+          if ($c == $cluster && $b != $bin) {
+            // Map this bin and cluster by reference.
+            $memcacheCache[$b] = &$memcacheCache[$bin];
+          }
+        }
+      }
+    }
+  }
+
+  return empty($memcacheCache[$bin]) ? FALSE : $memcacheCache[$bin];
+}
+
+function dmemcache_key($key, $bin = 'cache') {
+  static $prefix;
+  // memcache_key_prefix can be set in settings.php to support site namespaces
+  // in a multisite environment.
+  if (empty($prefix)) {
+    if ($prefix = variable_get('memcache_key_prefix', '')) {
+      $prefix .= '-';
+    }
+    // When simpletest is running, emulate the simpletest database prefix here
+    // to avoid the child site setting cache entries in the parent site.
+    if (isset($GLOBALS['drupal_test_info']['test_run_id'])) {
+      $prefix .= $GLOBALS['drupal_test_info']['test_run_id'];
+    }
+  }
+  $full_key = urlencode($prefix . $bin . '-' . $key);
+
+  // Memcache only supports key lengths up to 250 bytes.  If we have generated
+  // a longer key, hash it with sha1 which will shrink the key down to 40 bytes
+  // while still keeping it unique.
+  if (strlen($full_key) > 250) {
+    $full_key = $prefix . $bin . '-' . sha1($key);
+  }
+
+  return $full_key;
+}
diff --git a/sites/all/modules/memcache/memcache-lock.inc b/sites/all/modules/memcache/memcache-lock.inc
new file mode 100644
index 0000000000000000000000000000000000000000..5d252840668f0240c92e82d558d39af0257475a5
--- /dev/null
+++ b/sites/all/modules/memcache/memcache-lock.inc
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * @file
+ * A memcache based implementation of a locking mechanism.
+ * See includes/lock.inc for documenation
+ */
+
+// Set up a define to make the code more readable, so we know we're setting a
+// value for our lock and not simply passing dmemcache a flag.
+define('LOCK_VALUE', TRUE);
+
+
+/**
+ * Initialize the locking system.
+ */
+function lock_initialize() {
+  global $locks;
+
+  $locks = array();
+}
+
+/**
+ * Acquire (or renew) a lock, but do not block if it fails.
+ *
+ * @param $name
+ *   The name of the lock.
+ * @param $timeout
+ *   A number of seconds (int) before the lock expires (minimum of 1).
+ * @return
+ *   TRUE if the lock was acquired, FALSE if it failed.
+ */
+function lock_acquire($name, $timeout = 30) {
+  global $locks;
+
+  // Ensure that the timeout is at least 1 sec. This is a limitation
+  // imposed by memcached.
+  $timeout = max($timeout, 1);
+  $now = microtime(TRUE);
+  $expire = $now + $timeout;
+
+  $result = dmemcache_get($name, 'semaphore');
+  if ($result && isset($locks[$name]) && $locks[$name] > $now) {
+    // Only renew the lock if we already set it and it has not expired.
+    if (dmemcache_set($name, LOCK_VALUE, $timeout, 'semaphore')) {
+      $locks[$name] = $expire;
+    }
+  }
+  else if (dmemcache_add($name, LOCK_VALUE, $timeout, 'semaphore')) {
+    $locks[$name] = $expire;
+  }
+  else {
+    // Failed to acquire the lock.  Unset the key from the $locks array even if
+    // not set, PHP 5+ allows this without error or warning.
+    unset($locks[$name]);
+  }
+
+  return isset($locks[$name]);
+}
+
+/**
+ * Check if lock acquired by a different process may be available.
+ *
+ * If an existing lock has expired, it is removed.
+ *
+ * @param $name
+ *   The name of the lock.
+ * @return
+ *   TRUE if there is no lock or it was removed, FALSE otherwise.
+ */
+function lock_may_be_available($name) {
+  return !dmemcache_get($name, 'semaphore');
+}
+
+/**
+ * Wait for a lock to be available.
+ *
+ * This function may be called in a request that fails to acquire a desired
+ * lock. This will block further execution until the lock is available or the
+ * specified delay in seconds is reached. This should not be used with locks
+ * that are acquired very frequently, since the lock is likely to be acquired
+ * again by a different request during the sleep().
+ *
+ * @param $name
+ *   The name of the lock.
+ * @param $delay
+ *   The maximum number of seconds to wait, as an integer.
+ * @return
+ *   TRUE if the lock holds, FALSE if it is available.
+ */
+function lock_wait($name, $delay = 30) {
+  $delay = (int) $delay;
+  while ($delay--) {
+    // This function should only be called by a request that failed to get a
+    // lock, so we sleep first to give the parallel request a chance to finish
+    // and release the lock.
+    sleep(1);
+    if (!dmemcache_get($name, 'semaphore')) {
+      // No longer need to wait.
+      return FALSE;
+    }
+  }
+  // The caller must still wait longer to get the lock.
+  return TRUE;
+}
+
+/**
+ * Release a lock previously acquired by lock_acquire().
+ *
+ * This will release the named lock if it is still held by the current request.
+ *
+ * @param $name
+ *   The name of the lock.
+ */
+function lock_release($name) {
+  global $locks;
+
+  dmemcache_delete($name, 'semaphore');
+  // We unset unconditionally since caller assumes lock is released anyway.
+  unset($locks[$name]);
+}
+
diff --git a/sites/all/modules/memcache/memcache-session.inc b/sites/all/modules/memcache/memcache-session.inc
new file mode 100644
index 0000000000000000000000000000000000000000..a0137aeec9a40805b4154bfc80e7379cebaf4d6e
--- /dev/null
+++ b/sites/all/modules/memcache/memcache-session.inc
@@ -0,0 +1,404 @@
+<?php
+
+require_once dirname(__FILE__) . '/dmemcache.inc';
+
+/**
+ * @file
+ * User session handling functions.
+ *
+ * An alternative to includes/session.inc.
+ */
+
+/**
+ * Implement hook_user() using a required module's namespace since memcache is 
+ * not a module and thus can't implement hooks directly.
+ */
+function filter_user($op, &$edit, &$account, $category = NULL) {
+  if ($op == 'update') {
+    // Invalidate cached user object.
+    cache_clear_all($account->uid, 'users');
+  }
+}
+
+function _drupal_session_open() {
+  return TRUE;
+}
+
+function _drupal_session_close() {
+  return TRUE;
+}
+
+function _drupal_session_read($key) {
+  global $user;
+
+  // Write and Close handlers are called after destructing objects since PHP 5.0.5
+  // Thus destructors can use sessions but session handler can't use objects.
+  // So we are moving session closure before destructing objects.
+  register_shutdown_function('session_write_close');
+
+  // Handle the case of first time visitors and clients that don't store
+  // cookies (eg. web crawlers).
+  if (!isset($_COOKIE[session_name()])) {
+    $user = drupal_anonymous_user();
+    return '';
+  }
+
+  // Otherwise, if the session is still active, we have a record of the
+  // client's session in memcache.
+  $session = dmemcache_get($key, 'session');
+
+  $user = _memcache_session_user_load($session);
+
+  // Record whether this session contains data so that in sess_write() it can
+  // be determined whether to skip a write.
+  $user->session_data_present_at_load = !empty($session->session);
+
+  return $user->session;
+}
+
+/**
+ * Write a session to session storage.
+ *
+ * We have the following cases to handle.
+ * 1. Anonymous user
+ *   1a. Without session data.
+ *   1b. With session data.
+ *   1c. Session saving has been turned off programatically
+ *       (see drupal_save_session()).
+ *   1d. Without session data but had session data at the beginning of the request
+ *       (thus a write must be made to clear stored session data).
+ * 2. Authenticated user.
+ *   2a. Without session data.
+ *   2b. With session data.
+ *   2c. Session saving has been turned off programatically
+ *       (see drupal_save_session()).
+ *
+ * @param $key
+ *   The session ID.
+ * @param $value
+ *   Any data to store in the session.
+ * @return
+ *   TRUE.
+ */
+function _drupal_session_write($key, $value) {
+  global $user;
+
+  if (!drupal_save_session()) {
+    return TRUE;
+  }
+
+  // Prepare the information to be saved.
+  $session = new stdClass;
+  $session->sid = $key;
+  $session->uid = $user->uid;
+  $session->cache = isset($user->cache) ? $user->cache : '';
+  $session->hostname = ip_address();
+  $session->session = $value;
+  $session->timestamp = REQUEST_TIME;
+
+  // Be sure that we have the latest user object.  If user_save() has been
+  // called, we need to refresh the object from the database.
+  $user = _memcache_session_user_load($session);
+
+  // If this is an authenticated user, or there is something to save in the
+  // session, or this is an anonymous user who currently has nothing in the
+  // session but did have something in session storage, write it to memcache.
+  // If $user->session_data_present_at_load is not set, the current user
+  // was created during this request and it's safest to do a write.
+  // Cases 1b, 1d, 2a, and 2b are covered here.
+  if ($user->uid || !empty($value) || empty($value) && (!isset($user->session_data_present_at_load) || $user->session_data_present_at_load)) {
+    dmemcache_set($key, $session, ini_get('session.gc_maxlifetime'), 'session');
+    if ($user->uid && $session->timestamp - $user->access > variable_get('session_write_interval', 360)) {
+      db_update('users')
+        ->fields(array('access' => $user->uid))
+        ->condition(array('uid' => $user->uid));
+      // Update the user access time so that the dmemcache_set() call
+      // caches the updated time.
+      $user->access = $session->timestamp;
+    }
+    // Data stored in session is stored in session memcache; no need
+    // to duplicate it in users memcache.
+    unset($user->session);
+    unset($user->session_data_present_at_load);
+    // Store the session id so we can locate the session with the user id.
+    $user->sid = $key;
+
+    dmemcache_set($user->uid, $user, ini_get('session.gc_maxlifetime'), 'users');
+  }
+
+  return TRUE;
+}
+
+
+/**
+ * Called by PHP session handling with the PHP session ID to end a user's session.
+ *
+ * @param  string $sid
+ *   the session id
+ */
+function _drupal_session_destroy($sid) {
+  dmemcache_delete($sid, 'session');
+  // Reset $_SESSION and $user to prevent a new session from being started
+  // in drupal_session_commit().
+  $_SESSION = array();
+  $user = drupal_anonymous_user();
+
+  // Unset the session cookies.
+  _drupal_session_delete_cookie(session_name());
+}
+
+function _drupal_session_garbage_collection($lifetime) {
+  // Automatic with memcached.
+  // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough
+  // value. For example, if you want user sessions to stay in your database
+  // for three weeks before deleting them, you need to set gc_maxlifetime
+  // to '1814400'. At that value, only after a user doesn't log in after
+  // three weeks (1814400 seconds) will his/her session be removed.
+  return TRUE;
+}
+
+/**
+ * Initialize the session handler, starting a session if needed.
+ */
+function drupal_session_initialize() {
+  global $user;
+
+  session_set_save_handler('_drupal_session_open', '_drupal_session_close', '_drupal_session_read', '_drupal_session_write', '_drupal_session_destroy', '_drupal_session_garbage_collection');
+
+  if (isset($_COOKIE[session_name()])) {
+    // If a session cookie exists, initialize the session. Otherwise the
+    // session is only started on demand in drupal_session_commit(), making
+    // anonymous users not use a session cookie unless something is stored in
+    // $_SESSION. This allows HTTP proxies to cache anonymous pageviews.
+    drupal_session_start();
+    if (!empty($user->uid) || !empty($_SESSION)) {
+      drupal_page_is_cacheable(FALSE);
+    }
+  }
+  else {
+    // Set a session identifier for this request. This is necessary because
+    // we lazyly start sessions at the end of this request, and some
+    // processes (like drupal_get_token()) needs to know the future
+    // session ID in advance.
+    $user = drupal_anonymous_user();
+    session_id(md5(uniqid('', TRUE)));
+  }
+}
+
+
+/**
+ * Counts how many users have sessions. Can count either anonymous sessions, authenticated sessions, or both.
+ * Would be insane slow with memcached as we would need to retrieve at least the stats of all object.
+ * Not implemented.
+ */
+function drupal_session_count($timestamp = 0, $anonymous = true) {
+}
+
+/**
+ * Forcefully start a session, preserving already set session data.
+ *
+ * @ingroup php_wrappers
+ */
+function drupal_session_start() {
+  if (!drupal_session_started()) {
+    // Save current session data before starting it, as PHP will destroy it.
+    $session_data = isset($_SESSION) ? $_SESSION : NULL;
+
+    session_start();
+    drupal_session_started(TRUE);
+
+    // Restore session data.
+    if (!empty($session_data)) {
+      $_SESSION += $session_data;
+    }
+  }
+}
+
+/**
+ * Commit the current session, if necessary.
+ *
+ * If an anonymous user already have an empty session, destroy it.
+ */
+function drupal_session_commit() {
+  global $user;
+
+  if (!drupal_save_session()) {
+    // We don't have anything to do if we are not allowed to save the session.
+    return;
+  }
+
+  if (empty($user->uid) && empty($_SESSION)) {
+    // There is no session data to store, destroy the session if it was
+    // previously started.
+    if (drupal_session_started()) {
+      session_destroy();
+    }
+  }
+  else {
+    // There is session data to store. Start the session if it is not already
+    // started.
+    if (!drupal_session_started()) {
+      drupal_session_start();
+    }
+    // Write the session data.
+    session_write_close();
+  }
+}
+
+/**
+ * Return whether a session has been started.
+ */
+function drupal_session_started($set = NULL) {
+  static $session_started = FALSE;
+  if (isset($set)) {
+    $session_started = $set;
+  }
+  return $session_started && session_id();
+}
+
+/**
+ * Called when an anonymous user becomes authenticated or vice-versa.
+ *
+ * @ingroup php_wrappers
+ */
+function drupal_session_regenerate() {
+  global $user;
+  if (drupal_session_started()) {
+    $old_session_id = session_id();
+    session_regenerate_id();
+    $new_session_id = session_id();
+  }
+  else {
+    // Start the session when it doesn't exist yet.
+    // Preserve the logged in user, as it will be reset to anonymous
+    // by _drupal_session_read.
+    $account = $user;
+    drupal_session_start();
+    $user = $account;
+  }
+
+  if (isset($old_session_id)) {
+    $info = dmemcache_get($old_session_id, 'session');
+    $info->sid = $new_session_id;
+    dmemcache_set($new_session_id, $info, ini_get('session.gc_maxlifetime'), 'session');
+    // Clear the old data from the cache.
+    dmemcache_delete($old_session_id, 'session');
+  }
+}
+
+
+/**
+ * End a specific user's session.
+ */
+function drupal_session_destroy_uid($uid) {
+  $user = dmemcache_get($uid, 'users');
+  if (is_object($user) && isset($user->sid)) {
+    dmemcache_delete($user->sid, 'session');
+  }
+  dmemcache_delete($uid, 'users');
+}
+
+
+/**
+ * Determine whether to save session data of the current request.
+ *
+ * This function allows the caller to temporarily disable writing of session data,
+ * should the request end while performing potentially dangerous operations, such as
+ * manipulating the global $user object.  See http://drupal.org/node/218104 for usage
+ *
+ * @param $status
+ *   Disables writing of session data when FALSE, (re-)enables writing when TRUE.
+ * @return
+ *   FALSE if writing session data has been disabled. Otherwise, TRUE.
+ */
+function drupal_save_session($status = NULL) {
+  $save_session = &drupal_static(__FUNCTION__, TRUE);
+  if (isset($status)) {
+    $save_session = $status;
+  }
+  return ($save_session);
+}
+
+/**
+ * Create the user object.
+ *
+ * @param $session
+ *   The session object (see sess_write() for the structure).
+ * @return $user
+ *   The user object.
+ */
+function _memcache_session_user_load($session) {
+  // We found the client's session record and they are an authenticated user.
+  if ($session && $session->uid != 0) {
+    $user = dmemcache_get($session->uid, 'users');
+    // If the 'users' memcache bin is unavailable, $user will be NULL.
+    // If the cached user was not found in the 'users' memcache bin, $user will
+    // be FALSE.
+    // In either of these cases, the user must be retrieved from the database.
+    if (!$user && isset($session->uid) && $session->uid != 0) {
+      $user = db_query('SELECT u.* FROM {users} u WHERE u.uid = :uid', array('uid' => $session->uid))->fetchObject();
+
+      if (!$user->status) {
+        $user = drupal_anonymous_user($session->session);
+      }
+      else {
+        $user = drupal_unpack($user);
+        // Normally we would join the session and user tables. But we already
+        // have the session information. So add that in.
+        $user->sid = $session->sid;
+        $user->cache = $session->cache;
+        $user->hostname = $session->hostname;
+        $user->timestamp = $session->timestamp;
+        $user->session = empty($session->session) ? '' : $session->session;
+
+        // Add roles element to $user
+        $user->roles = array();
+        $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+        $result = db_query("SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = :uid", array(':uid' => $user->uid));
+        while ($role = $result->fetchObject()) {
+          $user->roles[$role->rid] = $role->name;
+        }
+      }
+    }
+    else if ($user->uid) {
+      // Got a user object from 'users' memcache bin. Mark it in case modules
+      // want to know that this user was created from memcache.
+      $user->from_cache = TRUE;
+      $user->session = empty($session->session) ? '' : $session->session;
+    }
+    else {
+      // We will only get here when the session has a nonzero uid, a user object
+      // was successfully retrieved from the 'users' bin, and that user
+      // object's uid is 0. Not sure why this would ever happen. Leaving former
+      // comment in:
+      // This is a rare case that we have a session cached, but no session user object cached.
+      // This usually only happens if you kill memcached and restart it.
+      $user = drupal_anonymous_user($session->session);
+    }
+  }
+  // We didn't find the client's record (session has expired), or they are an
+  // anonymous user.
+  else  {
+    $session = isset($session->session) ? $session->session : '';
+    $user = drupal_anonymous_user($session);
+  }
+
+  return $user;
+}
+
+/**
+ * Deletes the session cookie.
+ *
+ * @param $name
+ *   Name of session cookie to delete.
+ * @param $force_insecure
+ *   Fornce cookie to be insecure.
+ */
+function _drupal_session_delete_cookie($name, $force_insecure = FALSE) {
+  if (isset($_COOKIE[$name])) {
+    $params = session_get_cookie_params();
+    setcookie($name, '', REQUEST_TIME - 3600, $params['path'], $params['domain'], !$force_insecure && $params['secure'], $params['httponly']);
+    unset($_COOKIE[$name]);
+  }
+}
+
diff --git a/sites/all/modules/memcache/memcache.inc b/sites/all/modules/memcache/memcache.inc
new file mode 100644
index 0000000000000000000000000000000000000000..7718880d2c6499ad2be9ce1ccba3dbea73a26bfe
--- /dev/null
+++ b/sites/all/modules/memcache/memcache.inc
@@ -0,0 +1,284 @@
+<?php
+
+require_once 'dmemcache.inc';
+
+/**
+ * Defines the period after which wildcard clears are not considered valid.
+ */
+define('MEMCACHE_WILDCARD_INVALIDATE', 86400 * 28);
+
+/** Implementation of cache.inc with memcache logic included **/
+
+class MemCacheDrupal implements DrupalCacheInterface {
+  function __construct($bin) {
+    $this->memcache = dmemcache_object($bin);
+    $this->bin = $bin;
+
+    $this->wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
+    $this->invalidate = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
+    $this->cache_lifetime = variable_get('cache_lifetime', 0);
+    $this->cache_flush = variable_get('cache_flush_' . $this->bin);
+    $this->flushed = min($this->cache_flush, REQUEST_TIME - $this->cache_lifetime);
+  }
+  function get($cid) {
+    $cache = dmemcache_get($cid, $this->bin, $this->memcache);
+    return $this->valid($cid, $cache) ? $cache : FALSE;
+  }
+
+  function getMultiple(&$cids) {
+    $results = dmemcache_get_multi($cids, $this->bin, $this->memcache);
+    foreach ($results as $cid => $result) {
+      if (!$this->valid($result->cid, $result)) {
+        // This object has expired, so don't return it.
+        unset($results[$cid]);
+      }
+      else {
+        // Remove items from the referenced $cids array that we are returning,
+        // per the comment in cache_get_multiple() in includes/cache.inc.
+        unset($cids[$result->cid]);
+      }
+    }
+    return $results;
+  }
+
+  protected function valid($cid, $cache) {
+    if (!isset($cache) || !is_object($cache)) {
+      return FALSE;
+    }
+
+    // The wildcard_valid() function has overhead due to a call to
+    // dmemcache_get_multi() to fetch possible wildcard flushes. Since some
+    // bins never have wildcard clears with a cid, we can shortcut these checks.
+    if (!empty($this->wildcard_flushes[$this->bin]) &&
+        max($this->wildcard_flushes[$this->bin]) >= (REQUEST_TIME - $this->invalidate) &&
+        !$this->wildcard_valid($cid, $cache))  {
+      return FALSE;
+    }
+
+    // Determine when the current bin was last flushed.
+    $item_flushed_globally = $cache->created && $this->cache_flush &&
+                             $this->cache_lifetime &&
+                             ($cache->created <= $this->flushed);
+
+    // Look in the session to determine if this item was flushed for the
+    // current user (ie, if they posted a comment and are viewing a cached page)
+    $cache_bins = isset($_SESSION['cache_flush']) ? $_SESSION['cache_flush'] : NULL;
+    $item_flushed_for_user = !empty($cache_bins) &&
+                             isset($cache_bins[$this->bin]) &&
+                             ($cache->created < $cache_bins[$this->bin]);
+    if ($item_flushed_for_user) {
+      return FALSE;
+    }
+
+    // The item can be expired if:
+    // - A liftetime is set and the item is older than both the lifetime and
+    //   the global flush.
+    // - The item has been create before the bin was flushed for this user.
+    // - The item could simply expire.
+    //
+    // For the two global cases we try and grab a lock.  If we get the lock, we
+    // return FALSE instead of the cached object which should cause it to be
+    // rebuilt.  If we do not get the lock, we return the cached object despite
+    // it's expired  The goal here is to avoid cache stampedes.
+    // By default the cache stampede semaphore is held for 15 seconds.  This
+    // can be adjusted by setting the memcache_stampede_semaphore variable.
+    $item_expired = isset($cache->expire) &&
+                    $cache->expire !== CACHE_PERMANENT &&
+                    $cache->expire <= REQUEST_TIME;
+    if ($item_flushed_globally || $item_expired) {
+      // To avoid a stampede, return TRUE despite the item being expired if
+      // a previous process set the stampede semaphore already. However only
+      // do this if the data is less than 30 minutes stale.
+      if ((REQUEST_TIME - $cache->expire) >= variable_get('memcache_max_staleness', 1800) ||
+          dmemcache_add($cid . '_semaphore', '', variable_get('memcache_stampede_semaphore', 15), $this->bin)) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+  function set($cid, $data, $expire = CACHE_PERMANENT, array $headers = NULL) {
+    $created = REQUEST_TIME;
+
+    // Create new cache object.
+    $cache = new stdClass;
+    $cache->cid = $cid;
+    $cache->data = is_object($data) ? clone $data : $data;
+    $cache->created = $created;
+    $cache->headers = $headers;
+    // Record the previous number of wildcard flushes affecting our cid.
+    $cache->flushes = $this->wildcard_flushes($cid);
+    if ($expire == CACHE_TEMPORARY) {
+      // Convert CACHE_TEMPORARY (-1) into something that will live in memcache
+      // until the next flush.
+      $cache->expire = REQUEST_TIME + 2591999;
+    }
+    // Expire time is in seconds if less than 30 days, otherwise is a timestamp.
+    else if ($expire != CACHE_PERMANENT && $expire < 2592000) {
+      // Expire is expressed in seconds, convert to the proper future timestamp
+      // as expected in dmemcache_get().
+      $cache->expire = REQUEST_TIME + $expire;
+    }
+    else {
+      $cache->expire = $expire;
+    }
+
+    // We manually track the expire time in $cache->expire.  When the object
+    // expires, we only allow one request to rebuild it to avoid cache
+    // stampedes. Other requests for the expired object while it is still being
+    // rebuilt get the expired object.
+    dmemcache_set($cid, $cache, 0, $this->bin, $this->memcache);
+  }
+
+  function clear($cid = NULL, $wildcard = FALSE) {
+    if (empty($cid) || $wildcard === TRUE) {
+      // system_cron() flushes all cache bins returned by hook_flush_caches()
+      // with cache_clear_all(NULL, $bin); This is for garbage collection with
+      // the database cache, but serves no purpose with memcache. So return
+      // early here.
+      if (!isset($cid)) {
+        return;
+      }
+      elseif ($cid == '*') {
+        $cid = '';
+      }
+      if ($this->cache_lifetime && empty($cid)) {
+        // Update the timestamp of the last global flushing of this bin.  When
+        // retrieving data from this bin, we will compare the cache creation
+        // time minus the cache_flush time to the cache_lifetime to determine
+        // whether or not the cached item is still valid.
+        variable_set("cache_flush_$this->bin", REQUEST_TIME);
+        $this->flushed = REQUEST_TIME;
+
+        // We store the time in the current user's session which is saved into
+        // the sessions table by sess_write().  We then simulate that the cache
+        // was flushed for this user by not returning cached data to this user
+        // that was cached before the timestamp.
+        if (isset($_SESSION['cache_flush']) && is_array($_SESSION['cache_flush'])) {
+          $cache_bins = $_SESSION['cache_flush'];
+        }
+        else {
+          $cache_bins = array();
+        }
+        $cache_bins[$this->bin] = REQUEST_TIME;
+        $_SESSION['cache_flush'] = $cache_bins;
+      }
+      else {
+        // Register a wildcard flush for current cid
+        $this->wildcards($cid, TRUE);
+      }
+    }
+    else {
+      $cids = is_array($cid) ? $cid : array($cid);
+      foreach ($cids as $cid) {
+        dmemcache_delete($cid, $this->bin, $this->memcache);
+      }
+    }
+  }
+
+  /**
+   * Sum of all matching wildcards.  Checking any single cache item's flush
+   * value against this single-value sum tells us whether or not a new wildcard
+   * flush has affected the cached item.
+   */
+  private function wildcard_flushes($cid) {
+    return array_sum($this->wildcards($cid));
+  }
+
+  /**
+   * Utilize multiget to retrieve all possible wildcard matches, storing
+   * statically so multiple cache requests for the same item on the same page
+   * load doesn't add overhead.
+   */
+  private function wildcards($cid, $flush = FALSE) {
+    static $wildcards = array();
+    $matching = array();
+
+    if (isset($this->wildcard_flushes[$this->bin]) &&
+      is_array($this->wildcard_flushes[$this->bin])) {
+      // Determine which lookups we need to perform to determine whether or not
+      // our cid was impacted by a wildcard flush.
+      $lookup = array();
+
+      // Find statically cached wildcards, and determine possibly matching
+      // wildcards for this cid based on a history of the lengths of past
+      // valid wildcard flushes in this bin.
+      foreach ($this->wildcard_flushes[$this->bin] as $length => $timestamp) {
+        if ($timestamp >= (REQUEST_TIME - $this->invalidate)) {
+          $key = '.wildcard-' . substr($cid, 0, $length);
+          $wildcard = dmemcache_key($key, $this->bin);
+          if (isset($wildcards[$this->bin][$wildcard])) {
+            $matching[$wildcard] = $wildcards[$this->bin][$wildcard];
+          }
+          else {
+            $lookup[$wildcard] = $key;
+          }
+        }
+      }
+
+      // Do a multi-get to retrieve all possibly matching wildcard flushes.
+      if (!empty($lookup)) {
+        $values = dmemcache_get_multi($lookup, $this->bin, $this->memcache);
+        if (is_array($values)) {
+          // Prepare an array of matching wildcards.
+          $matching = array_merge($matching, $values);
+          // Store matches in the static cache.
+          if (isset($wildcards[$this->bin])) {
+            $wildcards[$this->bin] = array_merge($wildcards[$this->bin], $values);
+          }
+          else {
+            $wildcards[$this->bin] = $values;
+          }
+          $lookup = array_diff_key($lookup, $values);
+        }
+
+        // Also store failed lookups in our static cache, so we don't have to
+        // do repeat lookups on single page loads.
+        foreach ($lookup as $wildcard => $key) {
+          $wildcards[$this->bin][$wildcard] = 0;
+        }
+      }
+    }
+    if ($flush) {
+      // Avoid too many calls to variable_set() by only recording a flush for
+      // a fraction of the wildcard invalidation variable, per cid length.
+      // Defaults to 28 / 4, or one week.
+      $length = strlen($cid);
+      if (!isset($this->wildcard_flushes[$this->bin][$length]) ||
+          ($_SERVER['REQUEST_TIME'] - $this->wildcard_flushes[$this->bin][$length] > $this->invalidate / 4)) {
+        $this->wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
+        $this->wildcard_flushes[$this->bin][$length] = $_SERVER['REQUEST_TIME'];
+        variable_set('memcache_wildcard_flushes', $this->wildcard_flushes);
+      }
+      $wildcard = dmemcache_key('.wildcard-' . $cid, $this->bin);
+      if (isset($wildcards[$this->bin][$wildcard]) && $wildcards[$this->bin][$wildcard] != 0) {
+        $this->memcache->increment($wildcard);
+        $wildcards[$this->bin][$wildcard]++;
+      }
+      else {
+        $wildcards[$this->bin][$wildcard] = 1;
+        dmemcache_set('.wildcard-' . $cid, '1', 0, $this->bin);
+      }
+    }
+    return $matching;
+  }
+
+  /**
+   * Check if a wildcard flush has invalidated the current cached copy.
+   */
+  private function wildcard_valid($cid, $cache) {
+    // Previously cached content won't have ->flushes defined.  We could
+    // force flush, but instead leave this up to the site admin.
+    $flushes = isset($cache->flushes) ? (int)$cache->flushes : 0;
+    if ($flushes < (int)$this->wildcard_flushes($cid)) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
+  function isEmpty() {
+    // We do not know so err on the safe side?
+    return FALSE;
+  }
+}
+
diff --git a/sites/all/modules/memcache/memcache.info b/sites/all/modules/memcache/memcache.info
new file mode 100644
index 0000000000000000000000000000000000000000..b04fbcbe95aa3ee265fa14f2cd02650dbd29a802
--- /dev/null
+++ b/sites/all/modules/memcache/memcache.info
@@ -0,0 +1,14 @@
+name = Memcache
+description = High performance integration with memcache.
+package = Caching
+core = 7.x
+files[] = memcache.inc
+files[] = memcache.module
+files[] = memcache.test
+
+; Information added by drupal.org packaging script on 2011-05-27
+version = "7.x-1.0-beta4"
+core = "7.x"
+project = "memcache"
+datestamp = "1306500116"
+
diff --git a/sites/all/modules/memcache/memcache.test b/sites/all/modules/memcache/memcache.test
new file mode 100644
index 0000000000000000000000000000000000000000..89abd6b7f25a6f6472210be91512227af831fd3e
--- /dev/null
+++ b/sites/all/modules/memcache/memcache.test
@@ -0,0 +1,541 @@
+<?php
+
+class MemcacheTestCase extends DrupalWebTestCase {
+  protected $default_bin = 'cache';
+  protected $default_cid = 'test_temporary';
+  protected $default_value = 'MemcacheTest';
+
+  function setUp() {
+    include_once drupal_get_path('module', 'memcache') . '/memcache.inc';
+    variable_set('cache_default_class', 'MemCacheDrupal');
+    parent::setUp();
+  }
+
+  /**
+   * Check whether or not a cache entry exists.
+   *
+   * @param $cid
+   *   The cache id.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function checkCacheExists($cid, $var, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    $cache = cache_get($cid, $bin);
+
+    return isset($cache->data) && $cache->data == $var;
+  }
+
+  /**
+   * Assert or a cache entry exists.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+    if ($var == NULL) {
+      $var = $this->default_value;
+    }
+
+    $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
+  }
+
+  /**
+   * Assert or a cache entry has been removed.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+
+    $cache = cache_get($cid, $bin);
+    $this->assertFalse($cache, $message);
+  }
+
+  /**
+   * Perform the general wipe.
+   * @param $bin
+   *   The bin to perform the wipe on.
+   */
+  protected function generalWipe($bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    cache_clear_all(NULL, $bin);
+  }
+
+  /**
+   * Setup the lifetime settings for caching.
+   *
+   * @param $time
+   *   The time in seconds the cache should minimal live.
+   */
+  protected function setupLifetime($time) {
+    variable_set('cache_lifetime', $time);
+    variable_set('cache_flush', 0);
+  }
+}
+
+class MemCacheSavingCase extends MemcacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Memcache saving test',
+      'description' => 'Check our variables are saved and restored the right way.',
+      'group' => 'Memcache'
+    );
+  }
+
+  /**
+   * Test the saving and restoring of a string.
+   */
+  function testString() {
+    $this->checkVariable($this->randomName(100));
+  }
+
+  /**
+   * Test the saving and restoring of an integer.
+   */
+  function testInteger() {
+    $this->checkVariable(100);
+  }
+
+  /**
+   * Test the saving and restoring of a double.
+   */
+  function testDouble() {
+    $this->checkVariable(1.29);
+  }
+
+  /**
+   * Test the saving and restoring of an array.
+   */
+  function testArray() {
+    $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
+  }
+
+  /**
+   * Test the saving and restoring of an object.
+   */
+  function testObject() {
+    $test_object = new stdClass();
+    $test_object->test1 = $this->randomName(100);
+    $test_object->test2 = 100;
+    $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
+
+    cache_set('test_object', $test_object, 'cache');
+    $cache = cache_get('test_object', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.'));
+  }
+
+  /*
+   * Check or a variable is stored and restored properly.
+   **/
+  function checkVariable($var) {
+    cache_set('test_var', $var, 'cache');
+    $cache = cache_get('test_var', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
+  }
+}
+
+/**
+ * Test cache_get_multiple().
+ */
+class MemCacheGetMultipleUnitTest extends MemcacheTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Fetching multiple cache items',
+      'description' => 'Confirm that multiple records are fetched correctly.',
+      'group' => 'Memcache',
+    );
+  }
+
+  function setUp() {
+    $this->default_bin = 'cache_page';
+    parent::setUp();
+  }
+
+  /**
+   * Test cache_get_multiple().
+   */
+  function testCacheMultiple() {
+    $item1 = $this->randomName(10);
+    $item2 = $this->randomName(10);
+    cache_set('item1', $item1, $this->default_bin);
+    cache_set('item2', $item2, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.'));
+    $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.'));
+
+    // Fetch both records from the database with cache_get_multiple().
+    $item_ids = array('item1', 'item2');
+    $items = cache_get_multiple($item_ids, $this->default_bin);
+    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
+    $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.'));
+
+    // Remove one item from the cache.
+    cache_clear_all('item2', $this->default_bin);
+
+    // Confirm that only one item is returned by cache_get_multiple().
+    $item_ids = array('item1', 'item2');
+    $items = cache_get_multiple($item_ids, $this->default_bin);
+    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
+    $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.'));
+    $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.'));
+  }
+}
+
+/**
+ * Test cache clearing methods.
+ */
+class MemCacheClearCase extends MemcacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache clear test',
+      'description' => 'Check our clearing is done the proper way.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+    $this->default_bin = 'cache_page';
+    $this->default_value = $this->randomName(10);
+
+    parent::setUp();
+  }
+
+  /**
+   * Test clearing using a cid.
+   */
+  function testClearCid() {
+    cache_set('test_cid_clear', $this->default_value, $this->default_bin);
+
+    $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
+    cache_clear_all('test_cid_clear', $this->default_bin);
+
+    $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
+
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard false.'));
+    cache_clear_all('*', $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches still exists after clearing cid "*" with wildcard false.'));
+  }
+
+  /**
+   * Test clearing using wildcard.
+   */
+  function testClearWildcard() {
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard true.'));
+    cache_clear_all('*', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches removed after clearing cid "*" with wildcard true.'));
+
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid substring with wildcard true.'));
+    cache_clear_all('test_', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches removed after clearing cid substring with wildcard true.'));
+  }
+
+  /**
+   * Test clearing using an array.
+   */
+  function testClearArray() {
+    // Create three cache entries.
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear3', $this->default_value),
+                      t('Three cache entries were created.'));
+
+    // Clear two entries using an array.
+    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                       t('Two cache entries removed after clearing with an array.'));
+
+    $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
+                      t('Entry was not cleared from the cache'));
+
+    // Set the cache clear threshold to 2 to confirm that the full bin is cleared
+    // when the threshold is exceeded.
+    variable_set('cache_clear_threshold', 2);
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two cache entries were created.'));
+    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear2', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear3', $this->default_value),
+                       t('All cache entries removed when the array exceeded the cache clear threshold.'));
+  }
+}
+
+/**
+ * @file
+ * Provides SimpleTests for core session handling functionality.
+ */
+class MemcacheSessionTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Session tests',
+      'description' => 'Memcache session handling tests.',
+      'group' => 'Memcache'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('session_test');
+  }
+
+  /**
+   * Tests for drupal_save_session() and drupal_session_regenerate().
+   */
+  function testSessionSaveRegenerate() {
+    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE (inside of testing framework) when initially called with no arguments.'), t('Session'));
+    $this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
+    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
+    $this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
+    $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));
+
+    // Test session hardening code from SA-2008-044.
+    $user = $this->drupalCreateUser(array('access content'));
+
+    // Enable sessions.
+    $this->sessionReset($user->uid);
+
+    // Make sure the session cookie is set as HttpOnly.
+    $this->drupalLogin($user);
+    $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), t('Session cookie is set as HttpOnly.'));
+    $this->drupalLogout();
+
+    // Verify that the session is regenerated if a module calls exit
+    // in hook_user_login().
+    user_save($user, array('name' => 'session_test_user'));
+    $user->name = 'session_test_user';
+    $this->drupalGet('session-test/id');
+    $matches = array();
+    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
+    $this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.'));
+    $original_session = $matches[1];
+
+    // We cannot use $this->drupalLogin($user); because we exit in
+    // session_test_user_login() which breaks a normal assertion.
+    $edit = array(
+      'name' => $user->name,
+      'pass' => $user->pass_raw
+    );
+    $this->drupalPost('user', $edit, t('Log in'));
+    $this->drupalGet('user');
+    $pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
+    $this->_logged_in = $pass;
+
+    $this->drupalGet('session-test/id');
+    $matches = array();
+    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
+    $this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.'));
+    $this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.'));
+  }
+
+  /**
+   * Test data persistence via the session_test module callbacks. Also tests
+   * drupal_session_count() since session data is already generated here.
+   */
+  function testDataPersistence() {
+    $user = $this->drupalCreateUser(array('access content'));
+    // Enable sessions.
+    $this->sessionReset($user->uid);
+
+    $this->drupalLogin($user);
+
+    $value_1 = $this->randomName();
+    $this->drupalGet('session-test/set/' . $value_1);
+    $this->assertText($value_1, t('The session value was stored.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session'));
+
+    // Attempt to write over val_1. If drupal_save_session(FALSE) is working.
+    // properly, val_1 will still be set.
+    $value_2 = $this->randomName();
+    $this->drupalGet('session-test/no-set/' . $value_2);
+    $this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
+
+    // Switch browser cookie to anonymous user, then back to user 1.
+    $this->sessionReset();
+    $this->sessionReset($user->uid);
+    $this->assertText($value_1, t('Session data persists through browser close.'), t('Session'));
+
+    // Logout the user and make sure the stored value no longer persists.
+    $this->drupalLogout();
+
+    $this->sessionReset();
+    $this->drupalGet('session-test/get');
+    $this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session'));
+
+    // Now try to store some data as an anonymous user.
+    $value_3 = $this->randomName();
+    $this->drupalGet('session-test/set/' . $value_3);
+    $this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session'));
+
+    // Try to store data when drupal_save_session(FALSE).
+    $value_4 = $this->randomName();
+    $this->drupalGet('session-test/no-set/' . $value_4);
+    $this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
+
+    // Login, the data should persist.
+    $this->drupalLogin($user);
+    $this->sessionReset($user->uid);
+    $this->drupalGet('session-test/get');
+    $this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session'));
+
+    // Change session and create another user.
+    $user2 = $this->drupalCreateUser(array('access content'));
+    $this->sessionReset($user2->uid);
+    $this->drupalLogin($user2);
+  }
+
+  /**
+   * Test that empty anonymous sessions are destroyed.
+   */
+  function testEmptyAnonymousSession() {
+    // Verify that no session is automatically created for anonymous user.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+
+    // The same behavior is expected when caching is enabled.
+    variable_set('cache', CACHE_NORMAL);
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
+
+    // Start a new session by setting a message.
+    $this->drupalGet('session-test/set-message');
+    $this->assertSessionCookie(TRUE);
+    $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
+
+    // Display the message, during the same request the session is destroyed
+    // and the session cookie is unset.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(FALSE);
+    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
+    $this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
+    $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
+
+    // Verify that session was destroyed.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+    $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
+
+    // Verify that no session is created if drupal_save_session(FALSE) is called.
+    $this->drupalGet('session-test/set-message-but-dont-save');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+
+    // Verify that no message is displayed.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertNoText(t('This is a dummy message.'), t('The message was not saved.'));
+  }
+
+  /**
+   * Reset the cookie file so that it refers to the specified user.
+   *
+   * @param $uid User id to set as the active session.
+   */
+  function sessionReset($uid = 0) {
+    // Close the internal browser.
+    $this->curlClose();
+    $this->loggedInUser = FALSE;
+
+    // Change cookie file for user.
+    $this->cookieFile = file_directory_path('temporary') . '/cookie.' . $uid . '.txt';
+    $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile;
+    $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE;
+    $this->drupalGet('session-test/get');
+    $this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session'));
+  }
+
+  /**
+   * Assert whether the SimpleTest browser sent a session cookie.
+   */
+  function assertSessionCookie($sent) {
+    if ($sent) {
+      $this->assertNotNull($this->session_id, t('Session cookie was sent.'));
+    }
+    else {
+      $this->assertNull($this->session_id, t('Session cookie was not sent.'));
+    }
+  }
+
+  /**
+   * Assert whether $_SESSION is empty at the beginning of the request.
+   */
+  function assertSessionEmpty($empty) {
+    if ($empty) {
+      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.'));
+    }
+    else {
+      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.'));
+    }
+  }
+}
diff --git a/sites/all/modules/memcache/memcache_admin/memcache.js b/sites/all/modules/memcache/memcache_admin/memcache.js
new file mode 100644
index 0000000000000000000000000000000000000000..c6b79a0a9ca34bbacaebfba71b77c1fb2e45b20f
--- /dev/null
+++ b/sites/all/modules/memcache/memcache_admin/memcache.js
@@ -0,0 +1,7 @@
+
+// Global Killswitch
+if (Drupal.jsEnabled) {
+$(document).ready(function() {
+    $("body").append($("#memcache-devel"));
+  });
+}
diff --git a/sites/all/modules/memcache/memcache_admin/memcache_admin.info b/sites/all/modules/memcache/memcache_admin/memcache_admin.info
new file mode 100644
index 0000000000000000000000000000000000000000..b9d8fcb66915a18065b28ad1033a309af0b77e40
--- /dev/null
+++ b/sites/all/modules/memcache/memcache_admin/memcache_admin.info
@@ -0,0 +1,12 @@
+name = Memcache Admin
+description = Adds a User Interface to monitor the Memcache for this site.
+package = Caching
+core = 7.x
+files[] = memcache_admin.module
+
+; Information added by drupal.org packaging script on 2011-05-27
+version = "7.x-1.0-beta4"
+core = "7.x"
+project = "memcache"
+datestamp = "1306500116"
+
diff --git a/sites/all/modules/memcache/memcache_admin/memcache_admin.module b/sites/all/modules/memcache/memcache_admin/memcache_admin.module
new file mode 100644
index 0000000000000000000000000000000000000000..ee84db5cbb0e32c51e8e9fc714485909e2b19fdd
--- /dev/null
+++ b/sites/all/modules/memcache/memcache_admin/memcache_admin.module
@@ -0,0 +1,260 @@
+<?php
+
+/**
+ * For the collection of memcache stats. This small .js file makes sure that the
+ * HTML displaying the stats is inside of the <body> part of the HTML
+ * document.
+ */
+function memcache_admin_init() {
+  global $user;
+  if (($user->uid == 0) || strstr($_SERVER['PHP_SELF'], 'update.php') || strstr($_GET['q'], 'autocomplete')) {
+    // update.php relies on standard error handler
+  }
+  else {
+    if ($user->uid) {
+      drupal_add_js(drupal_get_path('module', 'memcache_admin'). '/memcache.js');
+    }
+    register_shutdown_function('memcache_admin_shutdown');
+  }
+}
+
+/**
+ * Implementation of hook_perm().
+ */
+function memcache_admin_permission() {
+  return array(
+    'access memcache statistics' => array(
+      'title' => t('Access memcache statistics'),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function memcache_admin_menu() {
+  $items['admin/config/system/memcache'] = array(
+    'title' => 'Memcache',
+    'description' => 'Show or hide memcache statistics at the bottom of each page.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('memcache_admin_admin_settings'),
+    'access arguments' => array('administer site configuration'),
+  );
+  $items['admin/reports/memcache'] = array(
+    'title' => 'Memcache status',
+    'description' => "View the statistics for this site's memcache.",
+    'page callback' => 'memcache_admin_stats',
+    'access arguments' => array('access memcache statistics'),
+    'weight' => 1,
+  );
+  $memache_servers = variable_get('memcache_servers', array());
+  $clusters = array();
+  foreach($memache_servers as $server => $cluster) {
+    $clusters[$cluster]['servers'][] = $server;
+    $clusters[$cluster]['bin'] = _memcache_admin_get_bin_for_cluster($cluster);
+  }
+
+  $count = 0;
+  foreach ($clusters as $cluster => $cluster_info) {
+    if ($cluster_info['bin']) {
+      if (empty($current_cluster)) {
+        $current_cluster = arg(3);
+        if (empty($current_cluster)) {
+          $current_cluster = $cluster;
+        }
+      }
+
+      $items['admin/reports/memcache/'. $cluster] = array(
+        'title' => $cluster,
+        'type' =>  $count == 0 ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
+        'page callback' => 'memcache_admin_stats',
+        'page arguments' => array($cluster),
+        'access arguments' => array('access memcache statistics'),
+        'weight' => $count,
+      );
+      $count++;
+
+      $sub_count = 0;
+      foreach (array('default', 'malloc', 'maps', 'slabs', 'items', 'sizes') as $type) {
+        $items['admin/reports/memcache/'. $cluster .'/'. $type] = array(
+          'type' => $type == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
+          'page callback' => 'memcache_admin_stats',
+          'page arguments' => array($cluster, $type),
+          'title' => $type,
+          'access arguments' => array('access memcache statistics'),
+          'weight' => $sub_count,
+        );
+        $sub_count++;
+      }
+    }
+  }
+
+  return $items;
+}
+
+/**
+ * Settings form.
+ */
+function memcache_admin_admin_settings() {
+  $form['show_memcache_statistics'] = array('#type' => 'checkbox',
+    '#title' => t('Show memcache statistics at the bottom of each page'),
+    '#default_value' => variable_get('show_memcache_statistics', 1),
+    '#description' => t("These statistics will be visible to users with the 'access memcache statistics' permission."),
+  );
+  return system_settings_form($form);
+}
+
+/**
+ * Memcahe Stats page
+ *
+ * @param string $cluster - which cluster to view?
+ * @param string $type - which type of stat, eg: default, malloc, maps, cachedump, slabs, items or sizes
+ * @return string
+ */
+function memcache_admin_stats($cluster = 'default', $type = 'default') {
+  $bin = _memcache_admin_get_bin_for_cluster($cluster);
+
+  if ($bin) {
+    $stats = dmemcache_stats($bin, $type);
+
+    if (is_array($stats) && count($stats)) {
+      $output = '';
+
+      foreach ($stats as $server => $values) {
+        if (is_array($values)) {
+          // Do some custome value tweaks for specific stat page types.
+          switch ($type) {
+            case 'default' :
+              $values['uptime'] = format_interval($values['uptime']);
+              $values['time'] = format_date($values['time']);
+              $values['bytes'] = format_size($values['bytes']);
+              $values['bytes_read'] = format_size($values['bytes_read']);
+              $values['bytes_written'] = format_size($values['bytes_written']);
+              $values['limit_maxbytes'] = format_size($values['limit_maxbytes']);
+
+              //Custom Entries
+              $values['hit_percentage'] = ($values['cmd_get'] > 0) 
+                ? number_format(100.0 * $values['get_hits'] / $values['cmd_get'], 2) . '%' 
+                : '0';
+
+              $mem_used = intval($values['bytes']) / (intval($values['limit_maxbytes']) * 1024);
+              $values['mem_used'] = number_format(100.0 * $mem_used, 2) . '%';
+              break;
+          }
+
+          $output .= theme('memcache_admin_stats_table', array('server' => $server, 'stats' => $values));
+        }
+        else {
+          drupal_set_message(t('Unable to get statistic from server %server', array('%server' => $server)));
+        }
+      }
+    }
+
+    else {
+      $output = '';
+      drupal_set_message(t('No available statistics for this bin.'));
+    }
+  }
+
+  return $output;
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function memcache_admin_theme() {
+  return array(
+    'memcache_admin_stats_table' => array(
+      'variables' => array('server' => NULL, 'stats' => NULL),
+    )
+  );
+}
+
+/**
+ * Theme function for rendering the output from memcache_admin_stats
+ *
+ * @param string $server - Server name:port for caption for the table
+ * @param array $stats - array of key/value string pairs for the table results
+ * @return string
+ */
+function theme_memcache_admin_stats_table($variables) {
+  $server = $variables['server'];
+  $stats = $variables['stats'];
+
+  $rows = array();
+
+  foreach ($stats as $key => $value) {
+    if (is_array($value)) {
+      $rs = array();
+      foreach ($value as $k => $v) {
+        $rs[] = array($k, $v);
+      }
+      $rows[] = array($key, theme('table', array('header' => array(), 'rows' => $rs)));
+    }
+    else {
+      $rows[] = array($key, $value);
+    }
+  }
+
+  return theme('table', array('header' => array(t('Property'), t('Value')), 'rows' => $rows, 'caption' => $server));
+}
+
+
+/**
+ * Retrieve the cluster for any given bin
+ *
+ * @param string $cluster - Cluster ID
+ * @return string
+ */
+function _memcache_admin_get_bin_for_cluster($cluster) {
+  static $cluster_map = array();
+
+  if (!isset($cluster_map[$cluster])) {
+    $memache_bins = variable_get('memcache_bins', array());
+    if ($mapping = array_search($cluster, $memache_bins)) {
+      $cluster_map[$cluster] = $mapping;
+    }
+    else {
+      $cluster_map[$cluster] = 'default';
+    }
+  }
+
+  return $cluster_map[$cluster];
+}
+
+/**
+ * See memcache_admin_init() which registers this function as a shutdown function.
+ * Displays memcache stats in the footer.
+ */
+function memcache_admin_shutdown() {
+  global $_memcache_statistics;
+
+  // Try not to break non-HTML pages.
+  if (function_exists('drupal_get_http_header')) {
+    $header = drupal_get_http_header('content-type');
+    if ($header) {
+      $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
+      foreach ($formats as $format) {
+        if (strstr($header, $format)) {
+          return;
+        }
+      }
+    }
+  }
+
+  if (variable_get('show_memcache_statistics', TRUE) && function_exists('user_access') && user_access('access memcache statistics')) {
+    if (!empty($_memcache_statistics)) {
+      foreach ($_memcache_statistics as $row => $stats) {
+        $_memcache_statistics[$row][1] = check_plain($stats[1]);
+        $_memcache_statistics[$row][2] = check_plain($stats[2]);
+      }
+
+      $variables = array('header' => array(t('Operation'), t('Bin'), t('Key'), t('Hit')),
+                         'rows' => $_memcache_statistics);
+      $output = theme('table', $variables);
+
+      // this makes sure all of the HTML is within the <body> even though this <script> is outside it
+      print '<div id="memcache-devel"><h2>'. t('Memcache statistics'). '</h2>'. $output. '</div>';
+    }
+  }
+}