// --- BEGIN COPYRIGHT BLOCK --- // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; // version 2.1 of the License. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301 USA // // Copyright (C) 2007 Red Hat, Inc. // All rights reserved. // --- END COPYRIGHT BLOCK --- #include #include #include #include "prmem.h" #include "prsystem.h" #include "plstr.h" #include "prio.h" #include "prprf.h" #include "main/ConfigStore.h" #include "main/Memory.h" #include "main/Util.h" #include "engine/RA.h" #ifdef XP_WIN32 #define TPS_PUBLIC __declspec(dllexport) #else /* !XP_WIN32 */ #define TPS_PUBLIC #endif /* !XP_WIN32 */ #ifdef XP_WIN32 #define TOKENDB_PUBLIC __declspec(dllexport) #else /* !XP_WIN32 */ #define TOKENDB_PUBLIC #endif /* !XP_WIN32 */ #ifdef __cplusplus extern "C" { #endif static PR_CALLBACK void* _AllocTable(void* pool, PRSize size) { return PR_MALLOC(size); } static PR_CALLBACK void _FreeTable(void* pool, void* item) { PR_DELETE(item); } static PR_CALLBACK PLHashEntry* _AllocEntry(void* pool, const void* key) { return PR_NEW(PLHashEntry); } static PR_CALLBACK void _FreeEntry(void* pool, PLHashEntry* he, PRUintn flag) { if( he == NULL ) { return; } if (flag == HT_FREE_VALUE) { if( he->value != NULL ) { PL_strfree( (char*) he->value ); he->value = NULL; } } else if (flag == HT_FREE_ENTRY) { if( he->key != NULL ) { PL_strfree( (char*) he->key ); he->key = NULL; } if( he->value != NULL ) { PL_strfree( (char*) he->value ); he->value = NULL; } PR_DELETE(he); } } static PLHashAllocOps _AllocOps = { _AllocTable, _FreeTable, _AllocEntry, _FreeEntry }; #ifdef __cplusplus } #endif ///// ConfigStoreRoot ConfigStoreRoot::ConfigStoreRoot() { m_set = PL_NewHashTable(3, PL_HashString, PL_CompareStrings, PL_CompareValues, &_AllocOps, NULL); m_set_refcount = 0; } // If the ConfigStoreRoot goes out of scope, we can't destroy // the Hashtable because others maybe depending on the values // inside. ConfigStoreRoot::~ConfigStoreRoot () { if( m_set != NULL ) { if (m_set_refcount==0) { PL_HashTableDestroy( m_set ); m_set = NULL; } } } void ConfigStoreRoot::addref() { m_set_refcount++; } void ConfigStoreRoot::release() { m_set_refcount--; } PLHashTable *ConfigStoreRoot::getSet() { return m_set; } // ConfigureStore ConfigStore::ConfigStore(ConfigStoreRoot* root, const char *subStoreName) { m_substore_name = PL_strdup(subStoreName); m_root = root; root->addref(); m_lock = PR_NewLock(); } ConfigStore::~ConfigStore () { if (m_substore_name != NULL) { PR_Free(m_substore_name); } if (m_cfg_file_path != NULL) { PR_Free(m_cfg_file_path); } m_root->release(); delete m_root; if (m_lock != NULL ) PR_DestroyLock(m_lock); } /* ConfigStore::ConfigStore(const ConfigStore &X) { m_substore_name = X.m_substore_name; m_root = X.m_root; m_root.addref(); } */ ConfigStore ConfigStore::GetSubStore(const char *substore) { char *newname=NULL; const char *name = m_substore_name; if (strlen(name)==0) { // this is the root newname = PL_strdup(substore); } else { newname = PR_smprintf("%s.%s",name,substore); } return ConfigStore(m_root,newname); } /** * Reads configuration file and puts name value * pair into the global hashtable. */ static int ReadLine(PRFileDesc *f, char *buf, int buf_len, int *removed_return) { char *cur = buf; int sum = 0; PRInt32 rc; *removed_return = 0; while (1) { rc = PR_Read(f, cur, 1); if (rc == -1 || rc == 0) break; if (*cur == '\r') { continue; } if (*cur == '\n') { *cur = '\0'; *removed_return = 1; break; } sum++; cur++; } return sum; } #define MAX_CFG_LINE_LEN 4096 ConfigStore *ConfigStore::CreateFromConfigFile(const char *cfg_path) { PRFileDesc *f = NULL; int removed_return; char line[MAX_CFG_LINE_LEN]; ConfigStoreRoot *root = NULL; ConfigStore *cfg = NULL; f = PR_Open(cfg_path, PR_RDWR, 00400|00200); if (f == NULL) goto loser; root = new ConfigStoreRoot(); cfg = new ConfigStore(root,""); while (1) { int n = ReadLine(f, line, MAX_CFG_LINE_LEN, &removed_return); if (n > 0) { if (line[0] == '#') // handle comment line continue; int c = 0; while ((c < n) && (line[c] != '=')) { c++; } if (c < n) { line[c] = '\0'; } else { continue; /* no '=', skip this line */ } cfg->Add(line, &line[c+1]); } else if (n == 0 && removed_return == 1) { continue; /* skip empty line */ } else { break; } } if( f != NULL ) { PR_Close( f ); f = NULL; } cfg->SetFilePath(cfg_path); loser: return cfg; } /** * Parses string of format "n1=v1&n2=v2..." * into a ConfigStore. */ ConfigStore *ConfigStore::Parse(const char *s, const char *separator) { char *pair; char *line = NULL; int i; int len; char *lasts = NULL; if (s == NULL) return NULL; ConfigStoreRoot *root = new ConfigStoreRoot(); ConfigStore *set= new ConfigStore(root,""); line = PL_strdup(s); pair = PL_strtok_r(line, separator, &lasts); while (pair != NULL) { len = strlen(pair); i = 0; while (1) { if (i >= len) { goto skip; } if (pair[i] == '\0') { goto skip; } if (pair[i] == '=') { pair[i] = '\0'; break; } i++; } set->Add(&pair[0], &pair[i+1]); skip: pair = PL_strtok_r(NULL, separator, &lasts); } if( line != NULL ) { PL_strfree( line ); line = NULL; } return set; } typedef struct { int index; char *key; } Criteria; typedef struct { PRCList list; char *key; } OrderedEntry_t; typedef struct { regex_t *regex; ConfigStore *store; } PatternEntry_t; #ifdef __cplusplus extern "C" { #endif static PRIntn CountLoop(PLHashEntry *he, PRIntn index, void *arg) { Criteria *criteria = (Criteria *)arg; criteria->index++; return HT_ENUMERATE_NEXT; } static PRIntn Loop(PLHashEntry *he, PRIntn index, void *arg) { Criteria *criteria = (Criteria *)arg; if (criteria != NULL && index == criteria->index) { criteria->key = (char *)he->key; return HT_ENUMERATE_STOP; } else { return HT_ENUMERATE_NEXT; } } /** * Called from PL_HashTableEnumerateEntries * A pointer to a PRCList (circular linked list) is passed in. * Once enumeration is complete, the PRCList will contain a lexically * ordered list of a copy of the keys in the hash. * The caller needs to free the copies */ static PRIntn OrderLoop(PLHashEntry *he, PRIntn index, void *arg) { PRCList *qp = (PRCList *)arg; OrderedEntry_t *entry; if (he != NULL) { entry = (OrderedEntry_t *) PR_Malloc(sizeof(OrderedEntry_t)); entry->key = PL_strdup((char *) he->key); if (index ==0) { PR_APPEND_LINK((PRCList *)entry, qp); return HT_ENUMERATE_NEXT; } PRCList *head = PR_LIST_HEAD(qp); PRCList *next; while (head != qp) { OrderedEntry_t *current = (OrderedEntry_t *) head; if (strcmp((char *) he->key, (char *) current->key) <=0) break; next = PR_NEXT_LINK(head); head = next; } PR_INSERT_BEFORE((PRCList*) entry, head); return HT_ENUMERATE_NEXT; } else { return HT_ENUMERATE_STOP; } } /** * Called from PL_HashTableEnumerateEntries * A pointer to a PatternEntry is passed in. A PatternEntry consists of * a pointer a regex_t and a pointer to a new config store. * Once enumeration is complete, the new config store will contain * all the parameters (key and values) whose keys match the regex. */ static PRIntn PatternLoop(PLHashEntry *he, PRIntn index, void *arg) { PatternEntry_t *entry = (PatternEntry_t *) arg; if (entry == NULL) { return HT_ENUMERATE_STOP; } regex_t *r = entry->regex; ConfigStore *store = entry->store; if ((r == NULL) || (store == NULL)) { return HT_ENUMERATE_STOP; } size_t no_sub = r->re_nsub+1; regmatch_t *result = NULL; result = (regmatch_t *) PR_Malloc(sizeof(regmatch_t) * no_sub); if ((he != NULL) && (he->key != NULL) && (he->value != NULL)) { if (regexec(r, (char *) he->key, no_sub, result, 0)==0) { // Found a match store->Add((const char*) he->key, (const char *) he->value); } } else { return HT_ENUMERATE_STOP; } if (result != NULL) PR_Free(result); return HT_ENUMERATE_NEXT; } #ifdef __cplusplus } #endif int ConfigStore::Size() { Criteria criteria; criteria.index = 0; criteria.key = NULL; PR_Lock(m_lock); PL_HashTableEnumerateEntries(m_root->getSet(), &CountLoop, &criteria); PR_Unlock(m_lock); return criteria.index; } const char *ConfigStore::GetNameAt(int pos) { Criteria criteria; criteria.index = pos; criteria.key = NULL; PR_Lock(m_lock); PL_HashTableEnumerateEntries(m_root->getSet(), &Loop, &criteria); PR_Unlock(m_lock); return criteria.key; } /** * Checks if a key is defined. */ int ConfigStore::IsNameDefined(const char *name) { if (m_root->getSet()!= NULL) { if (GetConfig(name) != NULL) return 1; } return 0; } void ConfigStore::SetFilePath(const char* cfg_file_path) { m_cfg_file_path = PL_strdup(cfg_file_path); } void ConfigStore::Add(const char *name, const char *value) { if (IsNameDefined(name)) { PR_Lock(m_lock); PL_HashTableRemove(m_root->getSet(), name); PL_HashTableAdd(m_root->getSet(), PL_strdup(name), PL_strdup(value)); PR_Unlock(m_lock); } else { PR_Lock(m_lock); PL_HashTableAdd(m_root->getSet(), PL_strdup(name), PL_strdup(value)); PR_Unlock(m_lock); } } void ConfigStore::Remove(const char *name) { if (IsNameDefined(name)) { PR_Lock(m_lock); PL_HashTableRemove(m_root->getSet(), name); PR_Unlock(m_lock); } } const char *ConfigStore::GetConfig(const char *name) { char buf[256]; char *ret; if (m_root->getSet() ==NULL) { return NULL; } if (PL_strlen(m_substore_name) == 0) { PL_strncpy(buf,name,256); } else { PR_snprintf(buf,256,"%s.%s",m_substore_name,name); } PR_Lock(m_lock); ret = (char *)PL_HashTableLookupConst(m_root->getSet(), buf); PR_Unlock(m_lock); return ret; } /** * Retrieves configuration value as integer. */ int ConfigStore::GetConfigAsInt(const char *name) { char *value = NULL; value = (char *)GetConfig(name); if (value == NULL) return 0; return atoi(value); } /** * Retrieves configuration value as integer. If name is * not defined, default value is returned. */ TPS_PUBLIC int ConfigStore::GetConfigAsInt(const char *name, int def) { char *value = NULL; value = (char *)GetConfig(name); if (value == NULL) return def; return atoi(value); } /** * Retrieves configuration value as unsigned integer. */ unsigned int ConfigStore::GetConfigAsUnsignedInt(const char *name) { char *value = NULL; int i = 0; value = (char *)GetConfig(name); if (value == NULL) { return 0; } i = atoi(value); if (i < 0) { return 0; } return i; } /** * Retrieves configuration value as unsigned integer. If name is * not defined, default value is returned. */ TPS_PUBLIC unsigned int ConfigStore::GetConfigAsUnsignedInt(const char *name, unsigned int def) { char *value = NULL; int i = 0; value = (char *)GetConfig(name); if (value == NULL) { return def; } i = atoi(value); if (i < 0) { return def; } return i; } /** * Retrieves configuration value as boolean. */ bool ConfigStore::GetConfigAsBool(const char *name) { char *value = NULL; value = (char *)GetConfig(name); if (value == NULL) return false; if (PL_CompareStrings("true", value) != 0) return true; else return false; } /** * Retrieves configuration value as boolean. If name is * not defined, default value is returned. */ TPS_PUBLIC bool ConfigStore::GetConfigAsBool(const char *name, bool def) { char *value = NULL; value = (char *)GetConfig(name); if (value == NULL) return def; if (PL_CompareStrings("true", value) != 0) return true; else if (PL_CompareStrings("false", value) != 0) return false; else return def; } /** * Retrieves configuration value as string. If key is * not defined, default value is returned. */ TOKENDB_PUBLIC const char *ConfigStore::GetConfigAsString(const char *name, const char *def) { char *value = NULL; value = (char *)GetConfig(name); if (value == NULL) return def; return value; } /** * Retrieves configuration value as string. */ TPS_PUBLIC const char *ConfigStore::GetConfigAsString(const char *name) { return (char *)GetConfig(name); } /** * Allow operator[] overloading for retrieval of config strings */ const char* ConfigStore::operator[](const char*name) { return GetConfigAsString(name); } Buffer *ConfigStore::GetConfigAsBuffer(const char *key) { return GetConfigAsBuffer(key, NULL); } Buffer *ConfigStore::GetConfigAsBuffer(const char *key, const char *def) { const char *value = NULL; value = (char *)GetConfig(key); if (value == NULL) { if (def == NULL) { return NULL; } else { return Util::Str2Buf(def); } } else { return Util::Str2Buf(value); } } /** * returns a string containing all the parameters in the ConfigStore hash set in the * format key1=value1&&key2=value2&& ... * The list will be lexically ordered by parameter key values. * The string needs to be freed by the caller. **/ TPS_PUBLIC const char* ConfigStore::GetOrderedList() { char *outstr = NULL; char *new_string = NULL; PRCList order_list; PR_INIT_CLIST(&order_list); PR_Lock(m_lock); PL_HashTableEnumerateEntries(m_root->getSet(), &OrderLoop, &order_list); PR_Unlock(m_lock); PRCList *current = PR_LIST_HEAD(&order_list); PRCList *next; outstr = (char*) PR_Malloc(128); int allocated = 128; int needed = 0; PR_snprintf(outstr, 128, ""); while (current != &order_list) { OrderedEntry_t *entry = (OrderedEntry_t *) current; const char *value = GetConfigAsString(entry->key, ""); if ((entry != NULL) && (entry->key != NULL)) { needed = PL_strlen(outstr) + PL_strlen(entry->key) + PL_strlen(value) + 4; if (allocated <= needed) { while (allocated <= needed) { allocated = allocated * 2; } new_string = (char *)PR_Malloc(allocated); PR_snprintf(new_string, allocated, "%s", outstr); PR_Free(outstr); outstr = new_string; } PL_strcat(outstr, entry->key); PL_strcat(outstr, "="); PL_strcat(outstr, value); // free the memory for the Ordered Entry PL_strfree(entry->key); } next = PR_NEXT_LINK(current); PR_REMOVE_AND_INIT_LINK(current); if (current != NULL) { PR_Free(current); } current = next; if (current != &order_list) PL_strcat(outstr, "&&"); } return outstr; } /** * Commits changes to the config file */ TPS_PUBLIC int ConfigStore::Commit(const bool backup, char *error_msg, int len) { char name_tmp[256], cdate[256], name_bak[256], bak_dir[256]; char basename[256], dirname[256]; PRFileDesc *ftmp = NULL; PRExplodedTime time; PRTime now; PRStatus status; if (m_cfg_file_path == NULL) { PR_snprintf(error_msg, len, "ConfigStore::Commit(): m_cfg_file_path is NULL!"); return 1; } if (strrchr(m_cfg_file_path, '/') != NULL) { PR_snprintf((char *) basename, 256, "%s", strrchr(m_cfg_file_path, '/') +1); PR_snprintf((char *) dirname, PL_strlen(m_cfg_file_path) - PL_strlen(basename), "%s", m_cfg_file_path); PL_strcat(dirname, (const char *) '\0'); } else { PR_snprintf((char *) basename, 256, "%s", m_cfg_file_path); PR_snprintf((char *) dirname, 256, "."); } PR_snprintf(bak_dir, 256, "%s/bak", dirname); now = PR_Now(); PR_ExplodeTime(now, PR_LocalTimeParameters, &time); PR_snprintf(cdate, 16, "%04d%02d%02d%02d%02d%02dZ", time.tm_year, (time.tm_month + 1), time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); PR_snprintf(name_tmp, 256, "%s.%s.tmp", m_cfg_file_path,cdate); PR_snprintf(name_bak, 256, "%s/%s.%s", bak_dir, basename, cdate); ftmp = PR_Open(name_tmp, PR_WRONLY| PR_CREATE_FILE, 00400|00200); if (ftmp == NULL) { // unable to create temporary config file PR_snprintf(error_msg, len, "ConfigStore::Commit(): unable to create temporary config file"); return 1; } PRCList order_list; PR_INIT_CLIST(&order_list); PR_Lock(m_lock); PL_HashTableEnumerateEntries(m_root->getSet(), &OrderLoop, &order_list); PR_Unlock(m_lock); PRCList *current = PR_LIST_HEAD(&order_list); PRCList *next; while (current != &order_list) { OrderedEntry_t *entry = (OrderedEntry_t *) current; PR_Write(ftmp, entry->key, PL_strlen(entry->key)); PR_Write(ftmp, "=", 1); const char *value = GetConfigAsString(entry->key, ""); PR_Write(ftmp, value, PL_strlen(value)); PR_Write(ftmp, "\n", 1); // free the memory for the Ordered Entry if (entry->key != NULL) PL_strfree(entry->key); next = PR_NEXT_LINK(current); PR_REMOVE_AND_INIT_LINK(current); if (current != NULL) { PR_Free(current); } current = next; } PR_Close(ftmp); if (backup) { // create the backup directory if it does not exist if (PR_Access(bak_dir, PR_ACCESS_EXISTS) != PR_SUCCESS) { PR_MkDir(bak_dir, 00770); } status = PR_Rename(m_cfg_file_path, name_bak); if (status != PR_SUCCESS) { // failed to back up CS.cfg } } if (PR_Access(m_cfg_file_path, PR_ACCESS_EXISTS) == PR_SUCCESS) { // backup is false, or backup failed status = PR_Delete(m_cfg_file_path); if (status != PR_SUCCESS) { // failed to delete old CS.cfg file PR_snprintf(error_msg, len, "ConfigStore::Commit(): unable to delete old CS.cfg file"); return 1; } } status = PR_Rename(name_tmp, m_cfg_file_path); if (status != PR_SUCCESS) { // failed to move tmp to CS.cfg // major badness - we now have only tmp file, no CS.cfg PR_snprintf(error_msg, len, "ConfigStore::Commit(): failed to move tmp file to CS.cfg"); return 1; } return 0; } /** * Takes in a string containing a regular expression. * Returns a new ConfigStore which contains only those parameters whose * keys match the pattern. * The new Configstore must of course be freed by the caller. **/ ConfigStore *ConfigStore::GetPatternSubStore(const char *pattern) { ConfigStoreRoot *root = NULL; ConfigStore *ret = NULL; PatternEntry_t entry; regex_t *regex = NULL; int err_no=0; /* For regerror() */ regex = (regex_t *) malloc(sizeof(regex_t)); memset(regex, 0, sizeof(regex_t)); if((err_no=regcomp(regex, pattern, 0))!=0) /* Compile the regex */ { // Error in computing the regex size_t length; char *buffer; length = regerror (err_no, regex, NULL, 0); buffer = (char *) PR_Malloc(length); regerror (err_no, regex, buffer, length); // PR_fprintf(m_dump_f, "%s\n", buffer); /* Print the error */ PR_Free(buffer); regfree(regex); return NULL; } entry.regex = regex; root = new ConfigStoreRoot(); ret = new ConfigStore(root, ""); entry.store = ret; PR_Lock(m_lock); PL_HashTableEnumerateEntries(m_root->getSet(), &PatternLoop, &entry); PR_Unlock(m_lock); /* cleanup */ //regfree(entry.regex); //entry.store = NULL; ret->SetFilePath(""); return ret; }