Re: PHP 4.0 Bug #5440 Updated: Major Design Flaw(s) in FilePro access

From: Date: Tue, 20 Feb 2001 13:27:26 +0000
Subject: Re: PHP 4.0 Bug #5440 Updated: Major Design Flaw(s) in FilePro access
References: 1  Groups: php.dev 
Request: Send a blank email to php-dev+get-47058@lists.php.net to get a copy of this message
I expected you will not have a problem that I have not gotten back to you, since it took you 6 mos to get back to me, and now I have to dig through archives to find the source code. The code is attached. filepro_is_deleted is the new function At 12:23 PM 2/20/01 +0000, you wrote:
ID: 5440 Updated by: sniper Reported By: gilem@wsg.net Old-Status: Feedback Status: Closed Old-Bug Type: filePro related Bug Type: Feature/Change Request Assigned To: Comments: No feedback. Changed to change request. Reopen if still a problem for you. --Jani Previous Comments: --------------------------------------------------------------------------- [2001-01-18 09:40:47] sniper@php.net If you have made some improvements for the code, please send a patch made against the latest CVS (cvs diff -u) and someone will commit it if it is good. --Jani --------------------------------------------------------------------------- [2000-07-07 20:32:08] gilem@wsg.net filepro access has some major design flaws. The two functions that are currently used to pull data from a db are filepro_rowcount, and filepro_retrieve. filepro_rowcount returns the number of rows in the db that are NOT deleted. filepro_retrieve returns a row/column of the database, regardless of whether or not the row is (not) deleted. This makes it impossible for php to actually retrieve only the undeleted rows from the table. Recommendations: We have added an additional function in order to check if a row is deleted (filepro_is_deleted), and are happy to contribute this code. However, because of the inefficiency of the current retrieve function, we recommend adding a new function to obsolete the retrieve function. This function should be written like the dbase getrecord function, that returns a deleted member along with all the fields in a given record. In addition, the filepro_rowcount function is essentially useless, since it gives you the number of undeleted records. In order to improve the results of someone who is currently using this to iterate through their db, it is recommended that this function be changed to return the total (deleted or undeleted) number of records. --------------------------------------------------------------------------- ATTENTION! Do NOT reply to this email! To reply, use the web interface found at http://bugs.php.net/?id=5440&edit=2
/* +----------------------------------------------------------------------+
   | PHP version 4.0                                                      |
+----------------------------------------------------------------------+
   | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                   |
+----------------------------------------------------------------------+
   | This source file is subject to version 2.02 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available at through the world-wide-web at                           |
   | http://www.php.net/license/2_02.txt.                                 |
| If you did not receive a copy of the PHP license and are unable to |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
+----------------------------------------------------------------------+
   | Authors: Chad Robinson <chadr@brttech.com>                           |
+----------------------------------------------------------------------+ filePro 4.x support developed by Chad Robinson, chadr@brttech.com Contact Chad Robinson at BRT Technical Services Corp. for details. filePro is a registered trademark by Fiserv, Inc. This file contains no code or information that is not freely available from the filePro web site at http://www.fileproplus.com/ */ #include "php.h" #include "safe_mode.h" #include "fopen-wrappers.h" #include <string.h> #ifdef PHP_WIN32 #include <windows.h> #else #include <sys/param.h> #endif #include <errno.h> #include "php_globals.h" #include "php_filepro.h" #if HAVE_FILEPRO typedef struct fp_field {
        char *name;
        char *format;
        int width;
        struct fp_field *next;
} FP_FIELD; #ifdef THREAD_SAFE DWORD FPTls; static int numthreads=0; typedef struct fp_global_struct{
        char *fp_database;
        signed int fp_fcount;
        signed int fp_keysize;
        FP_FIELD *fp_fieldlist;
}fp_global_struct; #define FP_GLOBAL(a) fp_globals->a #define FP_TLS_VARS \
        fp_global_struct *fp_globals; \
        fp_globals=TlsGetValue(FPTls);
#else #define FP_GLOBAL(a) a #define FP_TLS_VARS
static char *fp_database = NULL;                        /* Database directory */
static signed int fp_fcount = -1;                       /* Column count */
static signed int fp_keysize = -1;                      /* Size of key records */
static FP_FIELD *fp_fieldlist = NULL;           /* List of fields */
#endif PHP_MINIT_FUNCTION(filepro) { #ifdef THREAD_SAFE
        fp_global_struct *fp_globals;
#if defined(COMPILE_DL) || defined(COMPILE_DL_FILEPRO)
        CREATE_MUTEX(fp_mutex,"FP_TLS");
        SET_MUTEX(fp_mutex);
        numthreads++;
        if (numthreads==1){
        if ((FPTls=TlsAlloc())==0xFFFFFFFF){
                FREE_MUTEX(fp_mutex);
                return 0;
        }}
        FREE_MUTEX(fp_mutex);
#endif
        fp_globals = (fp_global_struct *) LocalAlloc(LPTR, sizeof(fp_global_struct));
        TlsSetValue(FPTls, (void *) fp_globals);
#endif
        FP_GLOBAL(fp_database)=NULL;
        FP_GLOBAL(fp_fcount)=-1;
        FP_GLOBAL(fp_keysize)=-1;
        FP_GLOBAL(fp_fieldlist)=NULL;
        return SUCCESS;
} PHP_MSHUTDOWN_FUNCTION(filepro) { #ifdef THREAD_SAFE
        fp_global_struct *fp_globals;
        fp_globals = TlsGetValue(FPTls);
        if (fp_globals != 0)
                LocalFree((HLOCAL) fp_globals);
#if defined(COMPILE_DL) || defined(COMPILE_DL_FILEPRO)
        SET_MUTEX(fp_mutex);
        numthreads--;
        if (!numthreads){
        if (!TlsFree(FPTls)){
                FREE_MUTEX(fp_mutex);
                return 0;
        }}
        FREE_MUTEX(fp_mutex);
#endif #endif
        return SUCCESS;
} function_entry filepro_functions[] = {
        PHP_FE(filepro, NULL)
        PHP_FE(filepro_rowcount, NULL)
        PHP_FE(filepro_fieldname, NULL)
        PHP_FE(filepro_fieldtype, NULL)
        PHP_FE(filepro_fieldwidth, NULL)
        PHP_FE(filepro_fieldcount, NULL)
        PHP_FE(filepro_retrieve, NULL)
        PHP_FE(filepro_is_deleted, NULL)
        {NULL, NULL, NULL}
}; zend_module_entry filepro_module_entry = {
        "filepro", filepro_functions, PHP_MINIT(filepro), PHP_MSHUTDOWN(filepro), NULL, NULL, NULL, STANDARD_MODULE_PROPERTIES
}; #if defined(COMPILE_DL) || defined(COMPILE_DL_FILEPRO) #include "dl/phpdl.h" ZEND_GET_MODULE(filepro) #if (WIN32|WINNT) && defined(THREAD_SAFE) /*NOTE: You should have an odbc.def file where you export DllMain*/ BOOL WINAPI DllMain(HANDLE hModule,
                      DWORD  ul_reason_for_call,
                      LPVOID lpReserved)
{
    switch( ul_reason_for_call ) {
    case DLL_PROCESS_ATTACH:
                if ((FPTls=TlsAlloc())==0xFFFFFFFF){
                        return 0;
                }
                break;
    case DLL_THREAD_ATTACH:
                break;
    case DLL_THREAD_DETACH:
                break;
        case DLL_PROCESS_DETACH:
                if (!TlsFree(FPTls)){
                        return 0;
                }
                break;
    }
    return 1;
} #endif #endif /* * LONG filePro(STRING directory) * * Read and verify the map file. We store the field count and field info * internally, which means we become unstable if you modify the table while * a user is using it! We cannot lock anything since Web connections don't * provide the ability to later unlock what we locked. Be smart, be safe. */ PHP_FUNCTION(filepro) {
        pval *dir;
        FILE *fp;
        char workbuf[256]; /* FIX - should really be the max filename length */
        char readbuf[256];
        char *strtok_buf = NULL;
        int i;
        FP_FIELD *new_field, *tmp;
        PLS_FETCH();
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &dir) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_string(dir);
        /* FIX - we should really check and free these if they are used! */
        FP_GLOBAL(fp_database) = NULL;
    FP_GLOBAL(fp_fieldlist) = NULL;
        FP_GLOBAL(fp_fcount) = -1;
    FP_GLOBAL(fp_keysize) = -1;
        sprintf(workbuf, "%s/map", dir->value.str.val);
        if (PG(safe_mode) && (!php_checkuid(workbuf, 2))) {
                RETURN_FALSE;
        }
        if (php_check_open_basedir(workbuf)) {
                RETURN_FALSE;
        }
        if (!(fp = V_FOPEN(workbuf, "r"))) {
                php_error(E_WARNING, "filePro: cannot open map: [%d] %s",
                                        errno, strerror(errno));
                RETURN_FALSE;
        }
        if (!fgets(readbuf, 250, fp)) {
                fclose(fp);
                php_error(E_WARNING, "filePro: cannot read map: [%d] %s",
                                        errno, strerror(errno));
                RETURN_FALSE;
        }
        /* Get the field count, assume the file is readable! */
        if (strcmp(php_strtok_r(readbuf, ":", &strtok_buf), "map")) {
                php_error(E_WARNING, "filePro: map file corrupt or encrypted");
                RETURN_FALSE;
        }
        FP_GLOBAL(fp_keysize) = atoi(php_strtok_r(NULL, ":", &strtok_buf));
        php_strtok_r(NULL, ":", &strtok_buf);
        FP_GLOBAL(fp_fcount) = atoi(php_strtok_r(NULL, ":", &strtok_buf));
    /* Read in the fields themselves */
        for (i = 0; i < FP_GLOBAL(fp_fcount); i++) {
                if (!fgets(readbuf, 250, fp)) {
                        fclose(fp);
                        php_error(E_WARNING, "filePro: cannot read map: [%d] %s",
                                                errno, strerror(errno));
                        RETURN_FALSE;
                }
                new_field = emalloc(sizeof(FP_FIELD));
                new_field->next = NULL;
                new_field->name = estrdup(php_strtok_r(readbuf, ":", &strtok_buf));
                new_field->width = atoi(php_strtok_r(NULL, ":", &strtok_buf));
                new_field->format = estrdup(php_strtok_r(NULL, ":", &strtok_buf));
                /* Store in forward-order to save time later */
                if (!FP_GLOBAL(fp_fieldlist)) {
                        FP_GLOBAL(fp_fieldlist) = new_field;
                } else {
                        for (tmp = FP_GLOBAL(fp_fieldlist); tmp; tmp = tmp->next) {
                                if (!tmp->next) {
                                        tmp->next = new_field;
                                        tmp = new_field;
                                }
                        }
                }
        }
        fclose(fp);
        FP_GLOBAL(fp_database) = estrndup(dir->value.str.val,dir->value.str.len);
        RETVAL_TRUE;
} /* * LONG filePro_rowcount(void) * * Count the used rows in the database. filePro just marks deleted records * as deleted; they are not removed. Since no counts are maintained we need * to go in and count records ourselves. <sigh> * * Errors return false, success returns the row count. */ PHP_FUNCTION(filepro_rowcount) {
        FILE *fp;
        char workbuf[MAXPATHLEN];
        char readbuf[256];
        int recsize = 0, records = 0;
        PLS_FETCH();
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 0) {
                WRONG_PARAM_COUNT;
        }
        if (!FP_GLOBAL(fp_database)) {
                php_error(E_WARNING,
                                        "filePro: must set database directory first!\n");
                RETURN_FALSE;
        }
        recsize = FP_GLOBAL(fp_keysize) + 19; /* 20 bytes system info -1 to save time later */
        /* Now read the records in, moving forward recsize-1 bytes each time */
        sprintf(workbuf, "%s/key", FP_GLOBAL(fp_database));
        if (PG(safe_mode) && (!php_checkuid(workbuf, 2))) {
                RETURN_FALSE;
        }
        if (php_check_open_basedir(workbuf)) {
                RETURN_FALSE;
        }
        if (!(fp = V_FOPEN(workbuf, "r"))) {
                php_error(E_WARNING, "filePro: cannot open key: [%d] %s",
                                        errno, strerror(errno));
                RETURN_FALSE;
        }
        while (!feof(fp)) {
                if (fread(readbuf, 1, 1, fp) == 1) {
                        if (readbuf[0])
                                records++;
                        fseek(fp, recsize, SEEK_CUR);
                }
        }
    fclose(fp);
        RETVAL_LONG(records);
} /* * STRING filePro_fieldname(LONG field_number) * * Errors return false, success returns the name of the field. */ PHP_FUNCTION(filepro_fieldname) {
        pval *fno;
        FP_FIELD *lp;
        int i;
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &fno) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_long(fno);
        if (!FP_GLOBAL(fp_database)) {
                php_error(E_WARNING,
                                        "filePro: must set database directory first!\n");
                RETURN_FALSE;
        }
        for (i = 0, lp = FP_GLOBAL(fp_fieldlist); lp; lp = lp->next, i++) {
                if (i == fno->value.lval) {
                        RETURN_STRING(lp->name,1);
                }
        }
        php_error(E_WARNING,
                                "filePro: unable to locate field number %d.\n",
                                fno->value.lval);
        RETVAL_FALSE;
} /* * STRING filePro_fieldtype(LONG field_number) * * Errors return false, success returns the type (edit) of the field */ PHP_FUNCTION(filepro_fieldtype) {
        pval *fno;
        FP_FIELD *lp;
        int i;
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &fno) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_long(fno);
        if (!FP_GLOBAL(fp_database)) {
                php_error(E_WARNING,
                                        "filePro: must set database directory first!\n");
                RETURN_FALSE;
        }
        for (i = 0, lp = FP_GLOBAL(fp_fieldlist); lp; lp = lp->next, i++) {
                if (i == fno->value.lval) {
                        RETURN_STRING(lp->format,1);
                }
        }
        php_error(E_WARNING,
                                "filePro: unable to locate field number %d.\n",
                                fno->value.lval);
        RETVAL_FALSE;
} /* * STRING filePro_fieldwidth(int field_number) * * Errors return false, success returns the character width of the field. */ PHP_FUNCTION(filepro_fieldwidth) {
        pval *fno;
        FP_FIELD *lp;
        int i;
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &fno) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_long(fno);
        if (!FP_GLOBAL(fp_database)) {
                php_error(E_WARNING,
                                        "filePro: must set database directory first!\n");
                RETURN_FALSE;
        }
        for (i = 0, lp = FP_GLOBAL(fp_fieldlist); lp; lp = lp->next, i++) {
                if (i == fno->value.lval) {
                        RETURN_LONG(lp->width);
                }
        }
        php_error(E_WARNING,
                                "filePro: unable to locate field number %d.\n",
                                fno->value.lval);
        RETVAL_FALSE;
} /* * LONG filePro_fieldcount(void) * * Errors return false, success returns the field count. */ PHP_FUNCTION(filepro_fieldcount) {
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 0) {
                WRONG_PARAM_COUNT;
        }
        if (!FP_GLOBAL(fp_database)) {
                php_error(E_WARNING,
                                        "filePro: must set database directory first!\n");
                RETURN_FALSE;
        }
        /* Read in the first line from the map file */
        RETVAL_LONG(FP_GLOBAL(fp_fcount));
} /* * STRING filePro_retrieve(int row_number, int field_number) * * Errors return false, success returns the datum. */ PHP_FUNCTION(filepro_retrieve) {
        pval *rno, *fno;
    FP_FIELD *lp;
    FILE *fp;
    char workbuf[MAXPATHLEN];
        char readbuf[1024]; /* FIX - Work out better buffering! */
    int i, fnum, rnum;
    long offset;
        PLS_FETCH();
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &rno, &fno) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        if (!FP_GLOBAL(fp_database)) {
                php_error(E_WARNING,
                                        "filePro: must set database directory first!\n");
                RETURN_FALSE;
        }
        convert_to_long(rno);
        convert_to_long(fno);
        fnum = fno->value.lval;
        rnum = rno->value.lval;
    if (rnum < 0 || fnum < 0 || fnum >= FP_GLOBAL(fp_fcount)) {
        php_error(E_WARNING, "filepro: parameters out of range");
                RETURN_FALSE;
    }
    offset = (rnum + 1) * (FP_GLOBAL(fp_keysize) + 20) + 20; /* Record location */
    for (i = 0, lp = FP_GLOBAL(fp_fieldlist); lp && i < fnum; lp = lp->next, i++) {
        offset += lp->width;
    }
    if (!lp) {
        php_error(E_WARNING, "filePro: cannot locate field");
                RETURN_FALSE;
    }
        /* Now read the record in */
        sprintf(workbuf, "%s/key", FP_GLOBAL(fp_database));
        if (PG(safe_mode) && (!php_checkuid(workbuf, 2))) {
                RETURN_FALSE;
        }
        if (php_check_open_basedir(workbuf)) {
                RETURN_FALSE;
        }
        if (!(fp = V_FOPEN(workbuf, "r"))) {
                php_error(E_WARNING, "filePro: cannot open key: [%d] %s",
                                        errno, strerror(errno));
            fclose(fp);
                RETURN_FALSE;
        }
    fseek(fp, offset, SEEK_SET);
        if (fread(readbuf, lp->width, 1, fp) != 1) {
        php_error(E_WARNING, "filePro: cannot read data: [%d] %s",
                                        errno, strerror(errno));
            fclose(fp);
                RETURN_FALSE;
    }
    readbuf[lp->width] = '\0';
    fclose(fp);
        RETURN_STRING(readbuf,1);
} /* * LONG filePro_is_deleted(int row_number) * * return 1 if deleted (or invalid), 0 if not deleted */ PHP_FUNCTION(filepro_is_deleted) {
        pval *rno;
    FP_FIELD *lp;
    FILE *fp;
    char workbuf[MAXPATHLEN];
        char readbuf[1024]; /* FIX - Work out better buffering! */
    int i, rnum;
    long offset;
        PLS_FETCH();
        FP_TLS_VARS;
        if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &rno) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        if (!FP_GLOBAL(fp_database)) {
                php_error(E_WARNING,
                                        "filePro: must set database directory first!\n");
                RETURN_TRUE;
        }
        convert_to_long(rno);
        rnum = rno->value.lval;
    if (rnum < 0) {
        php_error(E_WARNING, "filepro: parameters out of range");
                RETURN_TRUE;
    }
    offset = (rnum + 1) * (FP_GLOBAL(fp_keysize) + 20) ; /* Record location */
        /* Now read the record in */
        sprintf(workbuf, "%s/key", FP_GLOBAL(fp_database));
        if (PG(safe_mode) && (!php_checkuid(workbuf, 2))) {
                RETURN_TRUE;
        }
        if (php_check_open_basedir(workbuf)) {
                RETURN_TRUE;
        }
        if (!(fp = V_FOPEN(workbuf, "r"))) {
                php_error(E_WARNING, "filePro: cannot open key: [%d] %s",
                                        errno, strerror(errno));
            fclose(fp);
                RETURN_TRUE;
        }
    fseek(fp, offset, SEEK_SET);
        if (fread(readbuf, 1, 1, fp) != 1) {
        php_error(E_WARNING, "filePro: cannot read data: [%d] %s",
                                        errno, strerror(errno));
            fclose(fp);
                RETURN_TRUE;
    }
        if( readbuf[0] ){
                fclose(fp);
                RETURN_FALSE;
        }
        fclose(fp);
        RETURN_TRUE;
} #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ===========================================================
Michael Gile                                  gilem@wsg.net
President                                     (518)435-0682
Web Services Group                       http://www.wsg.net  


« previous php.dev (#47058) next »