diff --git a/CMakeLists.txt b/CMakeLists.txt index 7628780..f0847fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ include(cmake/Legacy.cmake) # output header files etc. specifically configured for this build configure_file(config.h.cmake include/config.h @ONLY) configure_file(src/agents/store/sql/create-store.s.cmake src/agents/store/sql/createstore.s @ONLY) -configure_file(src/agents/store/sql/create-store-1.s.cmake src/agents/store/sql/createstore-1.s @ONLY) +configure_file(src/agents/store/sql/create-cookie-1.s.cmake src/agents/store/sql/createcookie-1.s @ONLY) # tell compiler where to find Bongo's header files include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/) diff --git a/include/msgapi.h b/include/msgapi.h index 15c5c57..f24addc 100644 --- a/include/msgapi.h +++ b/include/msgapi.h @@ -42,19 +42,6 @@ EXPORT int MsgAuthAddUser(const char *user); EXPORT int MsgAuthUserList(char **list[]); EXPORT void MsgAuthUserListFree(char **list[]); -// Auth / cookie functions - -#define MSGAUTH_COOKIE_LEN XPLHASH_MD5_LENGTH + 1 - -typedef struct { - char token[MSGAUTH_COOKIE_LEN]; - uint64_t expiration; -} MsgAuthCookie; - -EXPORT BOOL MsgAuthCreateCookie(const char *username, MsgAuthCookie *cookie, uint64_t timeout); -EXPORT int MsgAuthFindCookie(const char *username, const char *token); -EXPORT BOOL MsgAuthDeleteCookie(const char *username, const char *token); - // SQL Routines typedef struct _MsgSQLStatement { diff --git a/src/agents/store/CMakeLists.txt b/src/agents/store/CMakeLists.txt index e4c0625..ca8e975 100644 --- a/src/agents/store/CMakeLists.txt +++ b/src/agents/store/CMakeLists.txt @@ -6,7 +6,7 @@ add_executable(bongostore auth.c calendar.c sql/createstore.s - sql/createstore-1.s + sql/createcookie-1.s command.c command-parsing.c contacts.c diff --git a/src/agents/store/command.c b/src/agents/store/command.c index 823c99e..f40cf80 100644 --- a/src/agents/store/command.c +++ b/src/agents/store/command.c @@ -571,6 +571,7 @@ StoreCommandLoop(StoreClient *client) case STORE_COMMAND_COOKIE: /* COOKIE [BAKE | NEW] */ /* COOKIE [CRUMBLE | DELETE] [] */ + /* COOKIE LIST */ if (TOKEN_OK != (ccode = RequireUser(client)) || TOKEN_OK != (ccode = CheckTokC(client, n, 2, 3))) @@ -591,6 +592,8 @@ StoreCommandLoop(StoreClient *client) !XplStrCaseCmp(tokens[1], "DELETE")) { ccode = StoreCommandCOOKIEDELETE(client, 3 == n ? tokens[2] : NULL); + } else if (!XplStrCaseCmp(tokens[1], "LIST")) { + ccode = StoreCommandCOOKIELIST(client); } else { ccode = ConnWriteStr(client->conn, MSG3000UNKNOWN); } diff --git a/src/agents/store/command.h b/src/agents/store/command.h index 18f0f5e..c68822f 100644 --- a/src/agents/store/command.h +++ b/src/agents/store/command.h @@ -153,6 +153,8 @@ CCode StoreCommandCOOKIENEW(StoreClient *client, uint64_t timeout); CCode StoreCommandCOOKIEDELETE(StoreClient *client, const char *token); +CCode StoreCommandCOOKIELIST(StoreClient *client); + CCode StoreCommandCOPY(StoreClient *client, StoreObject *object, StoreObject *collection); CCode StoreCommandCREATE(StoreClient *client, char *collection, uint64_t guid); diff --git a/src/agents/store/cookie.c b/src/agents/store/cookie.c index e7537b0..b0b3771 100644 --- a/src/agents/store/cookie.c +++ b/src/agents/store/cookie.c @@ -1,150 +1,243 @@ -/**************************************************************************** - * - * Copyright (c) 2006 Novell, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, contact Novell, Inc. - * - * To contact Novell about this file by physical or electronic mail, you - * may find current contact information at www.novell.com. - * - ****************************************************************************/ - #include #include #include -#include #include #include "stored.h" #include "messages.h" +extern const char *sql_create_cookie_1[]; // defined in sql/create-cookie-1.s.cmake + +static MsgSQLHandle * +OpenCookieDB(StoreClient *client) +{ + char path[XPL_MAX_PATH + 1]; + int current_version = 0; + + snprintf(path, XPL_MAX_PATH, "%s/cookies.db", MsgGetDir(MSGAPI_DIR_DBF, NULL, 0)); + + MsgSQLStatement stmt; + memset(&stmt, 0, sizeof(MsgSQLStatement)); + MsgSQLStatement *schema = NULL; + + MsgSQLHandle *cdb = MsgSQLOpen(path, &client->memstack, 3000); + + if (MsgSQLBeginTransaction(cdb)) return NULL; + + schema = MsgSQLPrepare(cdb, "PRAGMA user_version;", &stmt); + if (schema == NULL) goto aborttran; + + if (! MsgSQLResults(cdb, schema)) goto aborttran; + + current_version = MsgSQLResultInt(&stmt, 0); + MsgSQLEndStatement(&stmt); + MsgSQLFinalize(&stmt); + + if (current_version > 1) { + Log(LOG_ERROR, "Cookie DB created with newer version of store, cannot open"); + } + + if (current_version < 1) { + if (MsgSQLQuickExecute(cdb, (const char*)sql_create_cookie_1)) + goto aborttran; + + MsgSQLReset(cdb); + } + + if (MsgSQLCommitTransaction(cdb)) goto aborttran; + + return cdb; + +aborttran: + MsgSQLFinalize(&stmt); + MsgSQLAbortTransaction(cdb); + + return NULL; +} + CCode StoreCommandCOOKIENEW(StoreClient *client, uint64_t timeout) { - MsgAuthCookie cookie; - CCode ccode = 0; - assert(STORE_PRINCIPAL_USER == client->principal.type); - - if (MsgAuthCreateCookie(client->principal.name, &cookie, timeout)) { - ccode = ConnWriteF(client->conn, "1000 %.32s\r\n", cookie.token); - } else { - ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); - } - - return ccode; -} + MsgSQLStatement stmt; + MsgSQLStatement *ret; + memset(&stmt, 0, sizeof(MsgSQLStatement)); + + char token[50]; + xpl_hash_context context; + MsgSQLHandle *db = NULL; + + XplHashNew(&context, XPLHASH_MD5); + snprintf(token, sizeof(token) - 1, "%x%x", + (unsigned int) XplGetThreadID(), (unsigned int) time(NULL)); + XplHashWrite(&context, token, strlen(token)); + XplRandomData(token, 8); + XplHashWrite(&context, token, strlen(token)); + XplHashFinal(&context, XPLHASH_LOWERCASE, token, XPLHASH_MD5_LENGTH); + + uint64_t expiration = time(NULL) + timeout; + + db = OpenCookieDB(client); + if (db == NULL) goto abort; + if (MsgSQLBeginTransaction(db)) goto abort; + + ret = MsgSQLPrepare(db, "INSERT INTO cookies (user, cookie, expiration) VALUES (?, ?, ?);", &stmt); + MsgSQLBindString(&stmt, 1, client->principal.name, FALSE); + MsgSQLBindString(&stmt, 2, token, FALSE); + MsgSQLBindInt64(&stmt, 3, expiration); + + if (MsgSQLExecute(db, &stmt)) goto abort; + + MsgSQLFinalize(&stmt); + if (MsgSQLCommitTransaction(db)) + goto abort; + + MsgSQLClose(db); + + return ConnWriteF(client->conn, "1000 %.32s\r\n", token); + +abort: + MsgSQLFinalize(&stmt); + MsgSQLAbortTransaction(db); + if (db) MsgSQLClose(db); + return ConnWriteStr(client->conn, MSG5004INTERNALERR); +} CCode StoreCommandCOOKIEDELETE(StoreClient *client, const char *token) { - CCode ccode = 0; + assert(STORE_PRINCIPAL_USER == client->principal.type); - assert(STORE_PRINCIPAL_USER == client->principal.type); + MsgSQLStatement stmt; + MsgSQLStatement *ret; + memset(&stmt, 0, sizeof(MsgSQLStatement)); - if (MsgAuthDeleteCookie(client->principal.name, token)) { - ccode = ConnWriteStr(client->conn, MSG1000OK); - } else { - ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); - } - - return ccode; + MsgSQLHandle *db = OpenCookieDB(client); + if (db == NULL) goto abort; + if (MsgSQLBeginTransaction(db)) goto abort; + + if (token != NULL) { + ret = MsgSQLPrepare(db, "DELETE FROM cookies WHERE (expiration < ?) OR (user=? AND cookie=?);", &stmt); + MsgSQLBindString(&stmt, 3, token, FALSE); + } else { + ret = MsgSQLPrepare(db, "DELETE FROM cookies WHERE expiration < ? OR user=?;", &stmt); + } + MsgSQLBindInt64(&stmt, 1, time(NULL)); + MsgSQLBindString(&stmt, 2, client->principal.name, FALSE); + + if (MsgSQLExecute(db, &stmt)) goto abort; + + MsgSQLFinalize(&stmt); + if (MsgSQLCommitTransaction(db)) + goto abort; + + MsgSQLClose(db); + + return ConnWriteStr(client->conn, MSG1000OK); + +abort: + MsgSQLFinalize(&stmt); + MsgSQLAbortTransaction(db); + if (db) MsgSQLClose(db); + return ConnWriteStr(client->conn, MSG5004INTERNALERR); } -#if 0 -// this looks like multiple-store code -CCode -TunnelAuthCookie(StoreClient *client, - struct sockaddr_in *serv, char *user, char *token, int nouser) -{ - Connection *conn = NULL; - char buffer[1024]; - CCode ccode; - - conn = NMAPConnect(NULL, serv); - if (!conn) { - return ConnWriteStr(client->conn, MSG3242BADAUTH); - } - - ccode = NMAPAuthenticateWithCookie(conn, user, token, buffer, sizeof(buffer)); - switch (ccode) { - case 1000: - ccode = SelectUser(client, user, NULL, nouser); - break; - case -1: - break; - default: - if (ccode >= 1000 && ccode < 6000) { - ccode = ConnWriteF(client->conn, "%d %s\r\n", ccode, buffer); - } else { - XplConsolePrintf("Unexpected response from remote store: %s\r\n", - buffer); - ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); - } - break; - } - - NMAPQuit(conn); - ConnFree(conn); - - return ccode; -} -#endif - CCode StoreCommandAUTHCOOKIE(StoreClient *client, char *user, char *token, int nouser) { - CCode ccode; -// struct sockaddr_in serv; + CCode ccode; - if (StoreAgent.installMode) { - // don't allow cookie logins in installation mode. FIXME: better error message? - return ConnWriteStr(client->conn, MSG3242BADAUTH); - } + if (StoreAgent.installMode) + // don't allow cookie logins in installation mode. FIXME: better error message? + return ConnWriteStr(client->conn, MSG3242BADAUTH); - if (0 != MsgAuthFindUser(user)) { - XplConsolePrintf("Couldn't find user object for %s\r\n", user); - ccode = ConnWriteStr(client->conn, MSG3242BADAUTH); - XplDelay(2000); - goto finish; - } + if (0 != MsgAuthFindUser(user)) { + Log(LOG_INFO, "Couldn't find user object for %s\r\n", user); + + XplDelay(2000); + return ConnWriteStr(client->conn, MSG3242BADAUTH); + } - // FIXME: This checks for a non-local store. For Bongo 1.0, we assume a single store. -#if 0 - if (serv.sin_addr.s_addr != MsgGetHostIPAddress()) { - /* non-local store, need to verify against user's server */ - - ccode = TunnelAuthCookie(client, &serv, user, token, nouser); - goto finish; - } -#endif + MsgSQLStatement stmt; + MsgSQLStatement *find = NULL; + int result; + + MsgSQLHandle *db = OpenCookieDB(client); + + if (MsgSQLBeginTransaction(db)) return -2; + + memset(&stmt, 0, sizeof(MsgSQLStatement)); + find = MsgSQLPrepare(db, "SELECT id FROM cookies WHERE user = ? AND cookie = ? AND expiration > ?;", &stmt); + if (find == NULL) goto abort; + + MsgSQLBindString(find, 1, user, FALSE); + MsgSQLBindString(find, 2, token, FALSE); + MsgSQLBindInt64(find, 3, time(NULL)); + + result = MsgSQLResults(db, find); + if (result < 0) goto abort; + + MsgSQLFinalize(&stmt); + if (MsgSQLCommitTransaction(db)) + goto abort; - switch (MsgAuthFindCookie(user, token)) { - case 0: - XplDelay(2000); - ccode = ConnWriteStr(client->conn, MSG3242BADAUTH); - break; - case 1: - ccode = SelectUser(client, user, NULL, nouser); - break; - default: - ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); - break; - } + MsgSQLClose(db); -finish: - return ccode; + if (result == 0) { + XplDelay(2000); + ccode = ConnWriteStr(client->conn, MSG3242BADAUTH); + } else { + ccode = SelectUser(client, user, NULL, nouser); + } + + return ccode; + +abort: + MsgSQLFinalize(&stmt); + MsgSQLAbortTransaction(db); + if (db) MsgSQLClose(db); + return ConnWriteStr(client->conn, MSG5004INTERNALERR); } +CCode +StoreCommandCOOKIELIST(StoreClient *client) +{ + MsgSQLStatement stmt; + MsgSQLStatement *find = NULL; + + MsgSQLHandle *db = OpenCookieDB(client); + + if (MsgSQLBeginTransaction(db)) return -2; + + memset(&stmt, 0, sizeof(MsgSQLStatement)); + find = MsgSQLPrepare(db, "SELECT cookie, expiration FROM cookies WHERE user = ?", &stmt); + if (find == NULL) goto abort; + + MsgSQLBindString(find, 1, client->principal.name, FALSE); + + // loop results + while (MsgSQLResults(db, &stmt) > 0) { + char *token; + + MsgSQLResultTextPtr(&stmt, 0, &token); + uint64_t expiration = MsgSQLResultInt64(&stmt, 1); + + ConnWriteF(client->conn, "2001 %s " FMT_UINT64_DEC "\r\n", token, expiration); + } + + MsgSQLFinalize(&stmt); + if (MsgSQLCommitTransaction(db)) + goto abort; + + MsgSQLClose(db); + + return ConnWriteStr(client->conn, MSG1000OK); + +abort: + MsgSQLFinalize(&stmt); + MsgSQLAbortTransaction(db); + if (db) MsgSQLClose(db); + return ConnWriteStr(client->conn, MSG5004INTERNALERR); +} diff --git a/src/agents/store/object-model.c b/src/agents/store/object-model.c index 586f583..d54f080 100644 --- a/src/agents/store/object-model.c +++ b/src/agents/store/object-model.c @@ -27,7 +27,6 @@ #include "messages.h" extern const char *sql_create_store[]; // defined in sql/create-store.s.cmake -extern const char *sql_create_store_1[]; // defined in sql/create-store-1.s.cmake extern const StorePropValName StorePropTable[]; // defined in properties.c int ACLCheckOnGUID(StoreClient *client, uint64_t guid, int prop); @@ -151,8 +150,8 @@ StoreObjectDBCheckSchema(StoreClient *client, BOOL new_install) // deliberate fall-through to upgrade to next version case 0: // current database schema - if (MsgSQLQuickExecute(client->storedb, (const char*)sql_create_store_1)) - goto abort; + //if (MsgSQLQuickExecute(client->storedb, (const char*)sql_create_store_1)) + // goto abort; // current version, nothing to do break; default: diff --git a/src/agents/store/sql/create-cookie-1.s.cmake b/src/agents/store/sql/create-cookie-1.s.cmake new file mode 100644 index 0000000..748ed57 --- /dev/null +++ b/src/agents/store/sql/create-cookie-1.s.cmake @@ -0,0 +1,8 @@ +.section ".note.GNU-stack","",%progbits +.section ".rodata" +.globl sql_create_cookie_1 +.type sql_create_cookie_1,@object +sql_create_cookie_1: +.incbin "@CMAKE_CURRENT_SOURCE_DIR@/src/agents/store/sql/create-cookie-1.sql" +.byte 0 +.size sql_create_cookie_1, .-sql_create_cookie_1 diff --git a/src/agents/store/sql/create-store-1.sql b/src/agents/store/sql/create-cookie-1.sql similarity index 57% rename from src/agents/store/sql/create-store-1.sql rename to src/agents/store/sql/create-cookie-1.sql index 190a547..0a1113e 100644 --- a/src/agents/store/sql/create-store-1.sql +++ b/src/agents/store/sql/create-cookie-1.sql @@ -1,10 +1,12 @@ CREATE TABLE cookies ( - guid INTEGER NOT NULL UNIQUE, + id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT DEFAULT NULL, cookie TEXT DEFAULT NULL, expiration INTEGER DEFAULT 0 ); CREATE INDEX cookies_lookup ON cookies (user, cookie); +CREATE INDEX cookies_lookup_user ON cookies (user); +CREATE INDEX cookies_expiry ON cookies (expiration); PRAGMA user_version = 1; diff --git a/src/agents/store/sql/create-store-1.s.cmake b/src/agents/store/sql/create-store-1.s.cmake deleted file mode 100644 index 1d5df30..0000000 --- a/src/agents/store/sql/create-store-1.s.cmake +++ /dev/null @@ -1,8 +0,0 @@ -.section ".note.GNU-stack","",%progbits -.section ".rodata" -.globl sql_create_store_1 -.type sql_create_store_1,@object -sql_create_store_1: -.incbin "@CMAKE_CURRENT_SOURCE_DIR@/src/agents/store/sql/create-store-1.sql" -.byte 0 -.size sql_create_store_1, .-sql_create_store_1 diff --git a/src/libs/msgapi/CMakeLists.txt b/src/libs/msgapi/CMakeLists.txt index 14a4182..9600589 100644 --- a/src/libs/msgapi/CMakeLists.txt +++ b/src/libs/msgapi/CMakeLists.txt @@ -1,7 +1,6 @@ #StrictCompile() add_library(bongomsgapi SHARED - cookie.c msgaddr.c msgapi.c msgauth.c diff --git a/src/libs/msgapi/cookie.c b/src/libs/msgapi/cookie.c deleted file mode 100644 index 6f2cc24..0000000 --- a/src/libs/msgapi/cookie.c +++ /dev/null @@ -1,188 +0,0 @@ -/* Bongo Project licensing applies to this file, see COPYING - * (C) 2007 Alex Hudson - */ - -/** \file - * Single cookie implementation for all agents; this ensures any cookie - * is valid with any agent which recognises them. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Internal function: path to the cookie file for this user. -void -MsgAuthCookiePath(const char *username, char *path, size_t length) -{ - snprintf(path, length, "%s/%s", - MsgGetDir(MSGAPI_DIR_COOKIE, path, XPL_MAX_PATH), - username); -} - -/** - * Create a 'cookie': a password-substitute token, and an associated expiry - * SECURITY: We shouldn't create cookies for non-authenticated users - * \param username User whose token this will be - * \param cookie Reference to the MsgAuthCookie we're creating - * \param timeout When the token should expire - * \return Whether or not the cookie was created - */ -EXPORT BOOL -MsgAuthCreateCookie(const char *username, MsgAuthCookie *cookie, uint64_t timeout) -{ - char path[XPL_MAX_PATH+1]; - FILE *cookiefile = NULL; - long original_length; - size_t written; - xpl_hash_context context; - - // create the cookie - cookie->expiration = time(NULL) + timeout; - - XplHashNew(&context, XPLHASH_MD5); - snprintf(cookie->token, sizeof(cookie->token), "%x%x", - (unsigned int) XplGetThreadID(), - (unsigned int) time(NULL)); - XplHashWrite(&context, cookie->token, strlen(cookie->token)); - XplRandomData(cookie->token, 8); - XplHashWrite(&context, cookie->token, strlen(cookie->token)); - XplHashFinal(&context, XPLHASH_LOWERCASE, cookie->token, XPLHASH_MD5_LENGTH); - - // save it to our cookie list - MsgAuthCookiePath(username, path, XPL_MAX_PATH); - cookiefile = fopen(path, "a"); - if (! cookiefile) - return FALSE; - - original_length = ftell(cookiefile); - - written = fwrite(cookie, 1, sizeof(MsgAuthCookie), cookiefile); - if ((fclose(cookiefile) != 0) || (sizeof(MsgAuthCookie) != written)) { - truncate(path, original_length); - return FALSE; - } - - return TRUE; -} - -/** - * Find a cookie based on the token. If it exists, it's as good as auth'ing - * SECURITY: This is much like authentication. Be careful to timeout on bad auth? - * \param username User who claims this token is theirs - * \param token Token we want to look for - * \return 0 = bad auth, 1 = good auth, 2 = internal error - */ -EXPORT int -MsgAuthFindCookie(const char *username, const char *token) -{ - char path[XPL_MAX_PATH+1]; - FILE *cookiefile = NULL; - MsgAuthCookie cookie; - int result = 0; - BOOL expired_tokens = FALSE; - uint64_t now; - - now = time(NULL); - - MsgAuthCookiePath(username, path, XPL_MAX_PATH); - - cookiefile = fopen(path, "r"); - if (! cookiefile) return 2; - - while (fread(&cookie, 1, sizeof(MsgAuthCookie), cookiefile) == sizeof(MsgAuthCookie)) { - if (cookie.expiration < now) { - expired_tokens = TRUE; - continue; - } - if (!strncmp (cookie.token, token, MSGAUTH_COOKIE_LEN)) { - result = 1; - break; - } - } - - if (ferror(cookiefile)) - result = 2; - - fclose(cookiefile); - - if (expired_tokens) - // call this function with no token to remove expired tokens - MsgAuthDeleteCookie(username, ""); - - return result; -} - -/** - * Remove a token from the user's cookie store. Removes expired tokens as a side-effect. - * \param username User whose token this should be - * \param token Token we want rid of. - * \return Whether or not we succeeded. - */ -EXPORT BOOL -MsgAuthDeleteCookie(const char *username, const char *token) -{ - char path[XPL_MAX_PATH + 1]; - FILE *cookiefile = NULL; - long filelen; - int cookiecount, i; - MsgAuthCookie **cookieset; - uint64_t now; - - now = time(NULL); - - MsgAuthCookiePath(username, path, XPL_MAX_PATH); - - // first, read cookies into memory - cookiefile = fopen(path, "w"); - if (! cookiefile) return FALSE; - - if (fseek(cookiefile, 0, SEEK_END) != 0) { - fclose(cookiefile); - return FALSE; - } - - filelen = ftell(cookiefile); - cookiecount = filelen / sizeof(MsgAuthCookie); - cookieset = MemMalloc(filelen + 1); - if (fread(cookieset, 1, filelen, cookiefile) != filelen) { - fclose(cookiefile); - return FALSE; - } - - // next, clear the existing file - rewind(cookiefile); - ftruncate(fileno(cookiefile), 0); - - // now, go through memory writing out only the cookies we want to keep - for (i = 0; i < cookiecount; i++) { - MsgAuthCookie *cookie = cookieset[i]; - if (! strncmp(cookie->token, token, MSGAUTH_COOKIE_LEN)) { - // we want to delete this token - cookie->expiration = 0; - } - if (cookie->expiration < now) { - // this token is expired, so we don't want this. - cookie->expiration = 0; - } - if (cookie->expiration != 0) { - // we want to keep this token, so write it out - size_t written; - written = fwrite(cookie, 1, sizeof(MsgAuthCookie), cookiefile); - if (written != sizeof(MsgAuthCookie)) { - // ooops, this is pretty bad... - fclose(cookiefile); - return FALSE; - } - } - } - - fclose(cookiefile); - return TRUE; -}