2 * Copyright 2001-2005 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * shib-mysql-ccache.cpp: Shibboleth Credential Cache using MySQL.
20 * Created by: Derek Atkins <derek@ihtfp.com>
25 // eventually we might be able to support autoconf via cygwin...
26 #if defined (_MSC_VER) || defined(__BORLANDC__)
27 # include "config_win32.h"
33 # define SHIBMYSQL_EXPORTS __declspec(dllexport)
35 # define SHIBMYSQL_EXPORTS
42 #include <shib/shib-threads.h>
43 #include <shib-target/shib-target.h>
44 #include <log4cpp/Category.hh>
53 // wanted to use MySQL codes for this, but can't seem to get back a 145
54 #define isCorrupt(s) strstr(s,"(errno: 145)")
56 #ifdef HAVE_LIBDMALLOCXX
62 using namespace shibboleth;
63 using namespace shibtarget;
64 using namespace log4cpp;
66 #define PLUGIN_VER_MAJOR 3
67 #define PLUGIN_VER_MINOR 0
70 "CREATE TABLE state (" \
71 "cookie VARCHAR(64) PRIMARY KEY, " \
72 "application_id VARCHAR(255)," \
75 "addr VARCHAR(128)," \
78 "provider VARCHAR(256)," \
80 "authn_context TEXT," \
83 #define REPLAY_TABLE \
84 "CREATE TABLE replay (id VARCHAR(255) PRIMARY KEY, " \
85 "expires TIMESTAMP, " \
88 static const XMLCh Argument[] =
89 { chLatin_A, chLatin_r, chLatin_g, chLatin_u, chLatin_m, chLatin_e, chLatin_n, chLatin_t, chNull };
90 static const XMLCh cleanupInterval[] =
91 { chLatin_c, chLatin_l, chLatin_e, chLatin_a, chLatin_n, chLatin_u, chLatin_p,
92 chLatin_I, chLatin_n, chLatin_t, chLatin_e, chLatin_r, chLatin_v, chLatin_a, chLatin_l, chNull
94 static const XMLCh cacheTimeout[] =
95 { chLatin_c, chLatin_a, chLatin_c, chLatin_h, chLatin_e, chLatin_T, chLatin_i, chLatin_m, chLatin_e, chLatin_o, chLatin_u, chLatin_t, chNull };
96 static const XMLCh mysqlTimeout[] =
97 { chLatin_m, chLatin_y, chLatin_s, chLatin_q, chLatin_l, chLatin_T, chLatin_i, chLatin_m, chLatin_e, chLatin_o, chLatin_u, chLatin_t, chNull };
98 static const XMLCh storeAttributes[] =
99 { chLatin_s, chLatin_t, chLatin_o, chLatin_r, chLatin_e, chLatin_A, chLatin_t, chLatin_t, chLatin_r, chLatin_i, chLatin_b, chLatin_u, chLatin_t, chLatin_e, chLatin_s, chNull };
101 class MySQLBase : public virtual saml::IPlugIn
104 MySQLBase(const DOMElement* e);
105 virtual ~MySQLBase();
108 bool repairTable(MYSQL*&, const char* table);
110 log4cpp::Category* log;
114 const DOMElement* m_root; // can only use this during initialization
118 void createDatabase(MYSQL*, int major, int minor);
119 void upgradeDatabase(MYSQL*);
120 pair<int,int> getVersion(MYSQL*);
123 // Forward declarations
124 static void mysqlInit(const DOMElement* e, Category& log);
126 extern "C" void shib_mysql_destroy_handle(void* data)
128 MYSQL* mysql = (MYSQL*) data;
129 if (mysql) mysql_close(mysql);
132 MySQLBase::MySQLBase(const DOMElement* e) : m_root(e)
135 saml::NDC ndc("MySQLBase");
137 log = &(Category::getInstance("shibmysql.MySQLBase"));
139 m_mysql = ThreadKey::create(&shib_mysql_destroy_handle);
147 MySQLBase::~MySQLBase()
152 MYSQL* MySQLBase::getMYSQL()
155 saml::NDC ndc("getMYSQL");
158 // Do we already have a handle?
159 MYSQL* mysql=reinterpret_cast<MYSQL*>(m_mysql->getData());
163 // Connect to the database
164 mysql = mysql_init(NULL);
166 log->error("mysql_init failed");
168 throw SAMLException("MySQLBase::getMYSQL(): mysql_init() failed");
171 if (!mysql_real_connect(mysql, NULL, NULL, NULL, "shibd", 0, NULL, 0)) {
173 log->crit("mysql_real_connect failed: %s", mysql_error(mysql));
175 throw SAMLException("MySQLBase::getMYSQL(): mysql_real_connect() failed");
178 log->info("mysql_real_connect failed: %s. Trying to create", mysql_error(mysql));
180 // This will throw an exception if it fails.
181 createDatabase(mysql, PLUGIN_VER_MAJOR, PLUGIN_VER_MINOR);
185 pair<int,int> v=getVersion (mysql);
187 // Make sure we've got the right version
188 if (v.first != PLUGIN_VER_MAJOR || v.second != PLUGIN_VER_MINOR) {
190 // If we're capable, try upgrading on the fly...
191 if (v.first == 0 || v.first == 1 || v.first == 2) {
192 if (mysql_query(mysql, "DROP TABLE state")) {
193 log->error("error dropping old session state table: %s", mysql_error(mysql));
195 if (v.first==2 && mysql_query(mysql, "DROP TABLE replay")) {
196 log->error("error dropping old session state table: %s", mysql_error(mysql));
198 upgradeDatabase(mysql);
202 log->crit("Unknown database version: %d.%d", v.first, v.second);
203 throw SAMLException("MySQLBase::getMYSQL(): Unknown database version");
207 // We're all set.. Save off the handle for this thread.
208 m_mysql->setData(mysql);
212 bool MySQLBase::repairTable(MYSQL*& mysql, const char* table)
214 string q = string("REPAIR TABLE ") + table;
215 if (mysql_query(mysql, q.c_str())) {
216 log->error("Error repairing table %s: %s", table, mysql_error(mysql));
220 // seems we have to recycle the connection to get the thread to keep working
221 // other threads seem to be ok, but we should monitor that
223 m_mysql->setData(NULL);
228 void MySQLBase::createDatabase(MYSQL* mysql, int major, int minor)
230 log->info("creating database");
234 ms = mysql_init(NULL);
236 log->crit("mysql_init failed");
237 throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_init failed");
240 if (!mysql_real_connect(ms, NULL, NULL, NULL, NULL, 0, NULL, 0)) {
241 log->crit("cannot open DB file to create DB: %s", mysql_error(ms));
242 throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect failed");
245 if (mysql_query(ms, "CREATE DATABASE shibd")) {
246 log->crit("cannot create shibd database: %s", mysql_error(ms));
247 throw SAMLException("ShibMySQLCCache::createDatabase(): create db cmd failed");
250 if (!mysql_real_connect(mysql, NULL, NULL, NULL, "shibd", 0, NULL, 0)) {
251 log->crit("cannot open shibd database");
252 throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect to plugin db failed");
258 catch (SAMLException&) {
265 // Now create the tables if they don't exist
266 log->info("Creating database tables");
268 if (mysql_query(mysql, "CREATE TABLE version (major INT, minor INT)")) {
269 log->error ("error creating version: %s", mysql_error(mysql));
270 throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
273 if (mysql_query(mysql,STATE_TABLE)) {
274 log->error ("error creating state table: %s", mysql_error(mysql));
275 throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
278 if (mysql_query(mysql,REPLAY_TABLE)) {
279 log->error ("error creating replay table: %s", mysql_error(mysql));
280 throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
284 q << "INSERT INTO version VALUES(" << major << "," << minor << ")";
285 if (mysql_query(mysql, q.str().c_str())) {
286 log->error ("error setting version: %s", mysql_error(mysql));
287 throw SAMLException("ShibMySQLCCache::createDatabase(): version insert failed");
291 void MySQLBase::upgradeDatabase(MYSQL* mysql)
293 if (mysql_query(mysql,STATE_TABLE)) {
294 log->error ("error creating state table: %s", mysql_error(mysql));
295 throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error creating state table");
298 if (mysql_query(mysql,REPLAY_TABLE)) {
299 log->error ("error creating replay table: %s", mysql_error(mysql));
300 throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error creating replay table");
304 q << "UPDATE version SET major = " << PLUGIN_VER_MAJOR;
305 if (mysql_query(mysql, q.str().c_str())) {
306 log->error ("error updating version: %s", mysql_error(mysql));
307 throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error updating version");
311 pair<int,int> MySQLBase::getVersion(MYSQL* mysql)
313 // grab the version number from the database
314 if (mysql_query(mysql, "SELECT * FROM version"))
315 log->error ("Error reading version: %s", mysql_error(mysql));
317 MYSQL_RES* rows = mysql_store_result(mysql);
319 if (mysql_num_rows(rows) == 1 && mysql_num_fields(rows) == 2) {
320 MYSQL_ROW row = mysql_fetch_row(rows);
321 int major = row[0] ? atoi(row[0]) : -1;
322 int minor = row[1] ? atoi(row[1]) : -1;
323 log->debug("opening database version %d.%d", major, minor);
324 mysql_free_result(rows);
325 return make_pair(major,minor);
327 // Wrong number of rows or wrong number of fields...
328 log->crit("Houston, we've got a problem with the database...");
329 mysql_free_result (rows);
330 throw SAMLException("ShibMySQLCCache::getVersion(): version verification failed");
333 log->crit("MySQL Read Failed in version verification");
334 throw SAMLException("ShibMySQLCCache::getVersion(): error reading version");
337 static void mysqlInit(const DOMElement* e, Category& log)
339 static bool done = false;
341 log.info("MySQL embedded server already initialized");
344 log.info("initializing MySQL embedded server");
346 // Setup the argument array
347 vector<string> arg_array;
348 arg_array.push_back("shibboleth");
350 // grab any MySQL parameters from the config file
351 e=saml::XML::getFirstChildElement(e,shibtarget::XML::SHIBTARGET_NS,Argument);
353 auto_ptr_char arg(e->getFirstChild()->getNodeValue());
355 arg_array.push_back(arg.get());
356 e=saml::XML::getNextSiblingElement(e,shibtarget::XML::SHIBTARGET_NS,Argument);
359 // Compute the argument array
360 vector<string>::size_type arg_count = arg_array.size();
361 const char** args=new const char*[arg_count];
362 for (vector<string>::size_type i = 0; i < arg_count; i++)
363 args[i] = arg_array[i].c_str();
365 // Initialize MySQL with the arguments
366 mysql_server_init(arg_count, (char **)args, NULL);
372 class ShibMySQLCCache : public MySQLBase, virtual public ISessionCache, virtual public ISessionCacheStore
375 ShibMySQLCCache(const DOMElement* e);
376 virtual ~ShibMySQLCCache();
378 // Delegate all the ISessionCache methods.
380 const IApplication* application,
381 const IEntityDescriptor* source,
382 const char* client_addr,
383 const SAMLSubject* subject,
384 const char* authnContext,
385 const SAMLResponse* tokens
387 { return m_cache->insert(application,source,client_addr,subject,authnContext,tokens); }
388 ISessionCacheEntry* find(const char* key, const IApplication* application, const char* client_addr)
389 { return m_cache->find(key,application,client_addr); }
390 void remove(const char* key, const IApplication* application, const char* client_addr)
391 { m_cache->remove(key,application,client_addr); }
393 bool setBackingStore(ISessionCacheStore*) {return false;}
395 // Store methods handle the database work
398 const IApplication* application,
399 const ISessionCacheEntry* entry,
406 string& applicationId,
407 string& clientAddress,
410 string& authnContext,
417 HRESULT onRead(const char* key, time_t& accessed);
418 HRESULT onRead(const char* key, string& tokens);
419 HRESULT onUpdate(const char* key, const char* tokens=NULL, time_t accessed=0);
420 HRESULT onDelete(const char* key);
425 bool m_storeAttributes;
426 ISessionCache* m_cache;
427 CondWait* shutdown_wait;
429 Thread* cleanup_thread;
431 static void* cleanup_fcn(void*); // XXX Assumed an ShibMySQLCCache
434 ShibMySQLCCache::ShibMySQLCCache(const DOMElement* e) : MySQLBase(e), m_storeAttributes(false)
437 saml::NDC ndc("ShibMySQLCCache");
439 log = &(Category::getInstance("shibmysql.SessionCache"));
441 m_cache = dynamic_cast<ISessionCache*>(
442 SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::MemorySessionCacheType, e)
444 if (!m_cache->setBackingStore(this)) {
446 throw SAMLException("Unable to register MySQL cache plugin as a cache store.");
449 shutdown_wait = CondWait::create();
452 // Load our configuration details...
453 const XMLCh* tag=m_root->getAttributeNS(NULL,storeAttributes);
454 if (tag && *tag && (*tag==chLatin_t || *tag==chDigit_1))
455 m_storeAttributes=true;
457 // Initialize the cleanup thread
458 cleanup_thread = Thread::create(&cleanup_fcn, (void*)this);
461 ShibMySQLCCache::~ShibMySQLCCache()
464 shutdown_wait->signal();
465 cleanup_thread->join(NULL);
469 HRESULT ShibMySQLCCache::onCreate(
471 const IApplication* application,
472 const ISessionCacheEntry* entry,
479 saml::NDC ndc("onCreate");
482 // Get XML data from entry. Default is not to return SAML objects.
483 const char* context=entry->getAuthnContext();
484 pair<const char*,const SAMLSubject*> subject=entry->getSubject();
485 pair<const char*,const SAMLResponse*> tokens=entry->getTokens();
488 q << "INSERT INTO state VALUES('" << key << "','" << application->getId() << "',";
490 q << "NOW(),NOW(),'";
492 q << "FROM_UNIXTIME(" << created << "),NOW(),'";
493 q << entry->getClientAddress() << "'," << majorVersion << "," << minorVersion << ",'" << entry->getProviderId() << "','"
494 << subject.first << "','" << context << "',";
496 if (m_storeAttributes && tokens.first)
497 q << "'" << tokens.first << "')";
501 if (log->isDebugEnabled())
502 log->debug("SQL insert: %s", q.str().c_str());
504 // then add it to the database
505 MYSQL* mysql = getMYSQL();
506 if (mysql_query(mysql, q.str().c_str())) {
507 const char* err=mysql_error(mysql);
508 log->error("error inserting %s: %s", key, err);
509 if (isCorrupt(err) && repairTable(mysql,"state")) {
511 if (mysql_query(mysql, q.str().c_str())) {
512 log->error("error inserting %s: %s", key, mysql_error(mysql));
523 HRESULT ShibMySQLCCache::onRead(
525 string& applicationId,
526 string& clientAddress,
529 string& authnContext,
538 saml::NDC ndc("onRead");
541 log->debug("searching MySQL database...");
543 string q = string("SELECT application_id,UNIX_TIMESTAMP(ctime),UNIX_TIMESTAMP(atime),addr,major,minor,provider,subject,authn_context,tokens FROM state WHERE cookie='") + key + "' LIMIT 1";
545 MYSQL* mysql = getMYSQL();
546 if (mysql_query(mysql, q.c_str())) {
547 const char* err=mysql_error(mysql);
548 log->error("error searching for %s: %s", key, err);
549 if (isCorrupt(err) && repairTable(mysql,"state")) {
550 if (mysql_query(mysql, q.c_str()))
551 log->error("error retrying search for %s: %s", key, mysql_error(mysql));
555 MYSQL_RES* rows = mysql_store_result(mysql);
557 // Nope, doesn't exist.
561 // Make sure we got 1 and only 1 rows.
562 if (mysql_num_rows(rows) != 1) {
563 log->error("Database select returned wrong number of rows: %d", mysql_num_rows(rows));
564 mysql_free_result(rows);
568 log->debug("match found, tranfering data back into memory");
583 MYSQL_ROW row = mysql_fetch_row(rows);
584 applicationId=row[0];
585 created=atoi(row[1]);
586 accessed=atoi(row[2]);
587 clientAddress=row[3];
588 majorVersion=atoi(row[4]);
589 minorVersion=atoi(row[5]);
597 mysql_free_result(rows);
602 HRESULT ShibMySQLCCache::onRead(const char* key, time_t& accessed)
605 saml::NDC ndc("onRead");
608 log->debug("reading last access time from MySQL database");
610 string q = string("SELECT UNIX_TIMESTAMP(atime) FROM state WHERE cookie='") + key + "' LIMIT 1";
612 MYSQL* mysql = getMYSQL();
613 if (mysql_query(mysql, q.c_str())) {
614 const char* err=mysql_error(mysql);
615 log->error("error searching for %s: %s", key, err);
616 if (isCorrupt(err) && repairTable(mysql,"state")) {
617 if (mysql_query(mysql, q.c_str()))
618 log->error("error retrying search for %s: %s", key, mysql_error(mysql));
622 MYSQL_RES* rows = mysql_store_result(mysql);
624 // Nope, doesn't exist.
628 // Make sure we got 1 and only 1 rows.
629 if (mysql_num_rows(rows) != 1) {
630 log->error("database select returned wrong number of rows: %d", mysql_num_rows(rows));
631 mysql_free_result(rows);
635 MYSQL_ROW row = mysql_fetch_row(rows);
636 accessed=atoi(row[0]);
639 mysql_free_result(rows);
644 HRESULT ShibMySQLCCache::onRead(const char* key, string& tokens)
647 saml::NDC ndc("onRead");
650 if (!m_storeAttributes)
653 log->debug("reading cached tokens from MySQL database");
655 string q = string("SELECT tokens FROM state WHERE cookie='") + key + "' LIMIT 1";
657 MYSQL* mysql = getMYSQL();
658 if (mysql_query(mysql, q.c_str())) {
659 const char* err=mysql_error(mysql);
660 log->error("error searching for %s: %s", key, err);
661 if (isCorrupt(err) && repairTable(mysql,"state")) {
662 if (mysql_query(mysql, q.c_str()))
663 log->error("error retrying search for %s: %s", key, mysql_error(mysql));
667 MYSQL_RES* rows = mysql_store_result(mysql);
669 // Nope, doesn't exist.
673 // Make sure we got 1 and only 1 rows.
674 if (mysql_num_rows(rows) != 1) {
675 log->error("database select returned wrong number of rows: %d", mysql_num_rows(rows));
676 mysql_free_result(rows);
680 MYSQL_ROW row = mysql_fetch_row(rows);
685 mysql_free_result(rows);
690 HRESULT ShibMySQLCCache::onUpdate(const char* key, const char* tokens, time_t lastAccess)
693 saml::NDC ndc("onUpdate");
698 q << "UPDATE state SET atime=FROM_UNIXTIME(" << lastAccess << ")";
700 if (!m_storeAttributes)
702 q << "UPDATE state SET tokens=";
704 q << "'" << tokens << "'";
709 log->warn("onUpdate called with nothing to do!");
713 q << " WHERE cookie='" << key << "'";
715 MYSQL* mysql = getMYSQL();
716 if (mysql_query(mysql, q.str().c_str())) {
717 const char* err=mysql_error(mysql);
718 log->error("error updating %s: %s", key, err);
719 if (isCorrupt(err) && repairTable(mysql,"state")) {
721 if (mysql_query(mysql, q.str().c_str())) {
722 log->error("error updating %s: %s", key, mysql_error(mysql));
733 HRESULT ShibMySQLCCache::onDelete(const char* key)
736 saml::NDC ndc("onDelete");
739 // Remove from the database
740 string q = string("DELETE FROM state WHERE cookie='") + key + "'";
741 MYSQL* mysql = getMYSQL();
742 if (mysql_query(mysql, q.c_str())) {
743 const char* err=mysql_error(mysql);
744 log->error("error deleting entry %s: %s", key, err);
745 if (isCorrupt(err) && repairTable(mysql,"state")) {
747 if (mysql_query(mysql, q.c_str())) {
748 log->error("error deleting entry %s: %s", key, mysql_error(mysql));
759 void ShibMySQLCCache::cleanup()
762 saml::NDC ndc("cleanup");
765 Mutex* mutex = Mutex::create();
768 int timeout_life = 0;
770 // Load our configuration details...
771 const XMLCh* tag=m_root->getAttributeNS(NULL,cleanupInterval);
773 rerun_timer = XMLString::parseInt(tag);
775 // search for 'mysql-cache-timeout' and then the regular cache timeout
776 tag=m_root->getAttributeNS(NULL,mysqlTimeout);
778 timeout_life = XMLString::parseInt(tag);
780 tag=m_root->getAttributeNS(NULL,cacheTimeout);
782 timeout_life = XMLString::parseInt(tag);
785 if (rerun_timer <= 0)
786 rerun_timer = 300; // rerun every 5 minutes
788 if (timeout_life <= 0)
789 timeout_life = 28800; // timeout after 8 hours
793 MYSQL* mysql = getMYSQL();
795 log->info("cleanup thread started...Run every %d secs; timeout after %d secs", rerun_timer, timeout_life);
797 while (shutdown == false) {
798 shutdown_wait->timedwait(mutex, rerun_timer);
800 if (shutdown == true)
803 // Find all the entries in the database that haven't been used
804 // recently In particular, find all entries that have not been
805 // accessed in 'timeout_life' seconds.
807 q << "DELETE FROM state WHERE " << "UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(atime) >= " << timeout_life;
809 if (mysql_query(mysql, q.str().c_str())) {
810 const char* err=mysql_error(mysql);
811 log->error("error purging old records: %s", err);
812 if (isCorrupt(err) && repairTable(mysql,"state")) {
813 if (mysql_query(mysql, q.str().c_str()))
814 log->error("error re-purging old records: %s", mysql_error(mysql));
819 log->info("cleanup thread exiting...");
826 void* ShibMySQLCCache::cleanup_fcn(void* cache_p)
828 ShibMySQLCCache* cache = (ShibMySQLCCache*)cache_p;
830 // First, let's block all signals
831 Thread::mask_all_signals();
833 // Now run the cleanup process.
838 class MySQLReplayCache : public MySQLBase, virtual public IReplayCache
841 MySQLReplayCache(const DOMElement* e);
842 virtual ~MySQLReplayCache() {}
844 bool check(const XMLCh* str, time_t expires) {auto_ptr_XMLCh temp(str); return check(temp.get(),expires);}
845 bool check(const char* str, time_t expires);
848 MySQLReplayCache::MySQLReplayCache(const DOMElement* e) : MySQLBase(e)
851 saml::NDC ndc("MySQLReplayCache");
854 log = &(Category::getInstance("shibmysql.ReplayCache"));
857 bool MySQLReplayCache::check(const char* str, time_t expires)
860 saml::NDC ndc("check");
863 // Remove expired entries
864 string q = string("DELETE FROM replay WHERE expires < NOW()");
865 MYSQL* mysql = getMYSQL();
866 if (mysql_query(mysql, q.c_str())) {
867 const char* err=mysql_error(mysql);
868 log->error("Error deleting expired entries: %s", err);
869 if (isCorrupt(err) && repairTable(mysql,"replay")) {
871 if (mysql_query(mysql, q.c_str()))
872 log->error("Error deleting expired entries: %s", mysql_error(mysql));
876 string q2 = string("SELECT id FROM replay WHERE id='") + str + "'";
877 if (mysql_query(mysql, q2.c_str())) {
878 const char* err=mysql_error(mysql);
879 log->error("Error searching for %s: %s", str, err);
880 if (isCorrupt(err) && repairTable(mysql,"replay")) {
881 if (mysql_query(mysql, q2.c_str())) {
882 log->error("Error retrying search for %s: %s", str, mysql_error(mysql));
883 throw SAMLException("Replay cache failed, please inform application support staff.");
887 throw SAMLException("Replay cache failed, please inform application support staff.");
891 MYSQL_RES* rows = mysql_store_result(mysql);
892 if (rows && mysql_num_rows(rows)>0) {
893 mysql_free_result(rows);
898 q3 << "INSERT INTO replay VALUES('" << str << "'," << "FROM_UNIXTIME(" << expires << "))";
900 // then add it to the database
901 if (mysql_query(mysql, q3.str().c_str())) {
902 const char* err=mysql_error(mysql);
903 log->error("Error inserting %s: %s", str, err);
904 if (isCorrupt(err) && repairTable(mysql,"state")) {
906 if (mysql_query(mysql, q3.str().c_str())) {
907 log->error("Error inserting %s: %s", str, mysql_error(mysql));
908 throw SAMLException("Replay cache failed, please inform application support staff.");
912 throw SAMLException("Replay cache failed, please inform application support staff.");
918 /*************************************************************************
919 * The registration functions here...
922 IPlugIn* new_mysql_ccache(const DOMElement* e)
924 return new ShibMySQLCCache(e);
927 IPlugIn* new_mysql_replay(const DOMElement* e)
929 return new MySQLReplayCache(e);
932 extern "C" int SHIBMYSQL_EXPORTS saml_extension_init(void*)
934 // register this ccache type
935 SAMLConfig::getConfig().getPlugMgr().regFactory(shibtarget::XML::MySQLReplayCacheType, &new_mysql_replay);
936 SAMLConfig::getConfig().getPlugMgr().regFactory(shibtarget::XML::MySQLSessionCacheType, &new_mysql_ccache);
940 extern "C" void SHIBMYSQL_EXPORTS saml_extension_term()
944 SAMLConfig::getConfig().getPlugMgr().unregFactory(shibtarget::XML::MySQLReplayCacheType);
945 SAMLConfig::getConfig().getPlugMgr().unregFactory(shibtarget::XML::MySQLSessionCacheType);