RBOOL
    collector_18_init
    (
        HbsState* hbsState,
        rSequence config
    )
{
    RBOOL isSuccess = FALSE;

    rList extensions = NULL;
    rList patterns = NULL;
    RPCHAR strA = NULL;
    RPCHAR tmpA = NULL;
    RPWCHAR strW = NULL;
    RPWCHAR tmpW = NULL;
    RU32 maxSize = 0;
    RBOOL isCaseInsensitive = FALSE;

    if( NULL != hbsState )
    {
#ifdef RPAL_PLATFORM_WINDOWS
        // On Windows files and paths are not case sensitive.
        isCaseInsensitive = TRUE;
#endif

        if( NULL == config ||
            rSequence_getLIST( config, RP_TAGS_EXTENSIONS, &extensions ) ||
            rSequence_getLIST( config, RP_TAGS_PATTERNS, &patterns ) )
        {
            if( NULL != ( cacheMutex = rMutex_create() ) &&
                NULL != ( matcherA = obsLib_new( 0, 0 ) ) &&
                NULL != ( matcherW = obsLib_new( 0, 0 ) ) )
            {
                cacheSize = 0;
                if( NULL != config &&
                    rSequence_getRU32( config, RP_TAGS_MAX_SIZE, &maxSize ) )
                {
                    cacheMaxSize = maxSize;
                }
                else
                {
                    cacheMaxSize = MAX_CACHE_SIZE;
                }
                
                if( NULL != ( documentCache = HbsRingBuffer_new( 0, cacheMaxSize ) ) )
                {
                    if( NULL == config )
                    {
                        // As a default we'll cache all new files
                        obsLib_addPattern( matcherA, (RPU8)"", sizeof( RCHAR ), NULL );
                        obsLib_addPattern( matcherW, (RPU8)_WCH(""), sizeof( RWCHAR ), NULL );
                    }
                    else
                    {
                        // If a config was provided we'll cache only certain extensions
                        // specified.
                        while( rList_getSTRINGA( extensions, RP_TAGS_EXTENSION, &strA ) )
                        {
                            if( rpal_string_expand( strA, &tmpA ) )
                            {
                                obsLib_addStringPatternA( matcherA, tmpA, TRUE, isCaseInsensitive, NULL );
                                rpal_memory_free( tmpA );
                            }
                            if( NULL != ( strW = rpal_string_atow( strA ) ) )
                            {
                                if( rpal_string_expandw( strW, &tmpW ) )
                                {
                                    obsLib_addStringPatternW( matcherW, tmpW, TRUE, isCaseInsensitive, NULL );
                                    rpal_memory_free( tmpW );
                                }
                                rpal_memory_free( strW );
                            }
                        }

                        while( rList_getSTRINGW( extensions, RP_TAGS_EXTENSION, &strW ) )
                        {
                            if( rpal_string_expandw( strW, &tmpW ) )
                            {
                                obsLib_addStringPatternW( matcherW, tmpW, TRUE, isCaseInsensitive, NULL );
                                rpal_memory_free( tmpW );
                            }
                            if( NULL != ( strA = rpal_string_wtoa( strW ) ) )
                            {
                                if( rpal_string_expand( strA, &tmpA ) )
                                {
                                    obsLib_addStringPatternA( matcherA, tmpA, TRUE, isCaseInsensitive, NULL );
                                    rpal_memory_free( tmpA );
                                }
                                rpal_memory_free( strA );
                            }
                        }

                        while( rList_getSTRINGA( patterns, RP_TAGS_STRING_PATTERN, &strA ) )
                        {
                            if( rpal_string_expand( strA, &tmpA ) )
                            {
                                obsLib_addStringPatternA( matcherA, tmpA, FALSE, isCaseInsensitive, NULL );
                                rpal_memory_free( tmpA );
                            }
                            if( NULL != ( strW = rpal_string_atow( strA ) ) )
                            {
                                if( rpal_string_expandw( strW, &tmpW ) )
                                {
                                    obsLib_addStringPatternW( matcherW, tmpW, FALSE, isCaseInsensitive, NULL );
                                    rpal_memory_free( tmpW );
                                }
                                rpal_memory_free( strW );
                            }
                        }

                        while( rList_getSTRINGW( patterns, RP_TAGS_STRING_PATTERN, &strW ) )
                        {
                            if( rpal_string_expandw( strW, &tmpW ) )
                            {
                                obsLib_addStringPatternW( matcherW, tmpW, FALSE, isCaseInsensitive, NULL );
                                rpal_memory_free( tmpW );
                            }
                            if( NULL != ( strA = rpal_string_wtoa( strW ) ) )
                            {
                                if( rpal_string_expand( strA, &tmpA ) )
                                {
                                    obsLib_addStringPatternA( matcherA, tmpA, FALSE, isCaseInsensitive, NULL );
                                    rpal_memory_free( tmpA );
                                }
                                rpal_memory_free( strA );
                            }
                        }
                    }

                    if( rQueue_create( &createQueue, _freeEvt, 200 ) &&
                        notifications_subscribe( RP_TAGS_NOTIFICATION_FILE_CREATE, NULL, 0, createQueue, NULL ) &&
                        notifications_subscribe( RP_TAGS_NOTIFICATION_GET_DOCUMENT_REQ, NULL, 0, NULL, getDocument ) &&
                        rThreadPool_task( hbsState->hThreadPool, parseDocuments, NULL ) )
                    {
                        isSuccess = TRUE;
                    }
                }
            }

            if( !isSuccess )
            {
                notifications_unsubscribe( RP_TAGS_NOTIFICATION_FILE_CREATE, createQueue, NULL );
                notifications_unsubscribe( RP_TAGS_NOTIFICATION_GET_DOCUMENT_REQ, NULL, getDocument );
                rQueue_free( createQueue );
                createQueue = NULL;

                obsLib_free( matcherA );
                obsLib_free( matcherW );
                HbsRingBuffer_free( documentCache );
                matcherA = NULL;
                matcherW = NULL;
                documentCache = NULL;

                rMutex_free( cacheMutex );
                cacheMutex = NULL;
            }
        }
    }

    return isSuccess;
}
RPRIVATE
RVOID
    _searchForStrings
    (
        rList stringsFound,
        rList searchStrings,
        RPU8 pBuff,
        RU64 size,
        RU64 baseAddr,
        RU32 minLength,
        RU32 maxLength
    )
{
    RPU8 pCurr;
    RPU8 pEnd;
    RPCHAR pStartStr = NULL;
    RBOOL isChar;
    RPU16 pwCurr;
    RPU16 pwEnd;
    RBOOL isWChar;
    RPWCHAR pwStartStr = NULL;
    RPWCHAR thisStrW = NULL;
    rSequence newFoundStr;

    pCurr = pBuff;
    pEnd = pBuff + size;

    // currently we only deal with NULL terminated strings
    // start with ascii strings...
    while( pCurr < pEnd )
    {
        isChar = rpal_string_isprint( *pCurr );

        if( NULL == pStartStr && isChar )  // found the begining of a string
        {
            pStartStr = (RPCHAR)pCurr;
        }
        else if( NULL != pStartStr && ( !isChar || 0 == *pCurr ) ) // found the end of a string
        {
            // is string NULL or Non-Ascii terminated
            if( 0 == *pCurr || !rpal_string_charIsAscii( *pCurr ) )
            {
                // Null terminate it so we can use it like a normal string
                *pCurr = 0;

                // strlen is really pCurr - pStartStr
                if( (RU32)( (RPCHAR)pCurr - pStartStr ) >= minLength &&
                    (RU32)( (RPCHAR)pCurr - pStartStr ) <= maxLength ) // is string long enough
                {
                    // convert string to wide char for comparision
                    if( NULL != ( thisStrW = rpal_string_atow( pStartStr ) ) )
                    {
                        if( _isStringInList( searchStrings, thisStrW ) && NULL != ( newFoundStr = rSequence_new() ) )
                        {
                            rSequence_addSTRINGW( newFoundStr, RP_TAGS_STRING, thisStrW );
                            rSequence_addRU64( newFoundStr, RP_TAGS_MEMORY_ADDRESS, baseAddr + ( (RPU8)pStartStr - pBuff ) );

                            if( !rList_addSEQUENCE( stringsFound, newFoundStr ) )
                            {
                                rSequence_free( newFoundStr );
                            }
                        }
                        rpal_memory_free( thisStrW );
                    }
                }
            }
            pStartStr = NULL;
        }
        pCurr++;
    }
    // Now look for Unicode strings
    pwCurr = (RPU16)pBuff;
    pwEnd = pwCurr + ( size / 2 );
    while( pwCurr < pwEnd )
    {
        isWChar = rpal_string_isprintW( *pwCurr );

        if( NULL == pwStartStr && isWChar )  // found the begining of a string
        {
            pwStartStr = (RPWCHAR)pwCurr;
        }
        else if( NULL != pwStartStr && ( !isWChar || 0 == *pwCurr ) ) // found the end of a string
        {
            // is string NULL terminated
            if( 0 == *pwCurr )
            {
                // wcslen is really pCurr - pStartStr
                if( (RU32)( (RPWCHAR)pwCurr - pwStartStr ) >= minLength &&
                    (RU32)( (RPWCHAR)pwCurr - pwStartStr ) <= maxLength ) // is string long enough
                {

                    if( _isStringInList( searchStrings, pwStartStr ) && NULL != ( newFoundStr = rSequence_new() ) )
                    {
                        rSequence_addSTRINGW( newFoundStr, RP_TAGS_STRING, pwStartStr );
                        rSequence_addRU64( newFoundStr, RP_TAGS_MEMORY_ADDRESS, baseAddr + ( (RPU8)pwStartStr - pBuff ) );

                        if( !rList_addSEQUENCE( stringsFound, newFoundStr ) )
                        {
                            rSequence_free( newFoundStr );
                        }
                    }
                }
            }
            pwStartStr = NULL;
        }
        pwCurr++;
    }
}
static
RVOID
    getDocument
    (
        rpcm_tag notifId,
        rSequence notif
    )
{
    rSequence tmp = NULL;
    DocSearchContext ctx = { 0 };
    RU32 hashSize = 0;
    rList foundDocs = NULL;
    RBOOL isAAlloced = FALSE;
    RBOOL isWAlloced = FALSE;
    UNREFERENCED_PARAMETER( notifId );

    if( NULL != notif )
    {
        if( !rSequence_getSTRINGA( notif, RP_TAGS_STRING_PATTERN, &ctx.exprA ) )
        {
            ctx.exprA = NULL;
        }
        if( !rSequence_getSTRINGW( notif, RP_TAGS_STRING_PATTERN, &ctx.exprW ) )
        {
            ctx.exprW = NULL;
        }
        if( NULL != ctx.exprA && NULL == ctx.exprW )
        {
            ctx.exprW = rpal_string_atow( ctx.exprA );
            isWAlloced = TRUE;
        }
        if( NULL != ctx.exprW && NULL == ctx.exprA )
        {
            ctx.exprA = rpal_string_wtoa( ctx.exprW );
            isAAlloced = TRUE;
        }
        if( !rSequence_getBUFFER( notif, RP_TAGS_HASH, (RPU8*)&ctx.pHash, &hashSize ) ||
            sizeof( *ctx.pHash ) != hashSize )
        {
            // Unexpected hash size, let's not gamble 
            ctx.pHash = NULL;
        }
    }

    if( rMutex_lock( cacheMutex ) )
    {
        if( NULL != ( foundDocs = rList_new( RP_TAGS_FILE_INFO, RPCM_SEQUENCE ) ) )
        {
            while( HbsRingBuffer_find( documentCache, (HbsRingBufferCompareFunc)findDoc, &ctx, &tmp ) )
            {
                // TODO: optimize this since if we're dealing with large files
                // we will be temporarily using large amounts of duplicate memory.
                // We just need to do some shallow free of the datastructures
                // somehow.
                if( NULL != ( tmp = rSequence_duplicate( tmp ) ) )
                {
                    if( !rList_addSEQUENCE( foundDocs, tmp ) )
                    {
                        rSequence_free( tmp );
                    }
                }
            }

            if( !rSequence_addLIST( notif, RP_TAGS_FILES, foundDocs ) )
            {
                rList_free( foundDocs );
            }
        }

        rMutex_unlock( cacheMutex );

        notifications_publish( RP_TAGS_NOTIFICATION_GET_DOCUMENT_REP, notif );
    }

    if( isAAlloced )
    {
        rpal_memory_free( ctx.exprA );
    }
    if( isWAlloced )
    {
        rpal_memory_free( ctx.exprW );
    }
}
static
RVOID
    processCodeIdentA
    (
        RPCHAR name,
        CryptoLib_Hash* pFileHash,
        RU64 codeSize,
        rSequence originalEvent
    )
{
    CodeIdent ident = { 0 };
    rSequence notif = NULL;
    rSequence sig = NULL;
    RPWCHAR wPath = NULL;
    RPWCHAR cleanPath = NULL;

    ident.codeSize = codeSize;

    if( NULL != name )
    {
        CryptoLib_hash( name, rpal_string_strlen( name ) * sizeof( RCHAR ), &ident.nameHash );
    }

    if( NULL != pFileHash )
    {
        rpal_memory_memcpy( &ident.fileHash, pFileHash, sizeof( *pFileHash ) );
    }

    if( rMutex_lock( g_mutex ) )
    {
        if( rpal_bloom_addIfNew( g_knownCode, &ident, sizeof( ident ) ) )
        {
            rMutex_unlock( g_mutex );

            if( NULL != ( notif = rSequence_new() ) )
            {
                hbs_markAsRelated( originalEvent, notif );

                if( ( rSequence_addSTRINGA( notif, RP_TAGS_FILE_PATH, name ) ||
                      rSequence_addSTRINGA( notif, RP_TAGS_DLL, name ) ||
                      rSequence_addSTRINGA( notif, RP_TAGS_EXECUTABLE, name ) ) &&
                    rSequence_addRU32( notif, RP_TAGS_MEMORY_SIZE, (RU32)codeSize ) &&
                    rSequence_addTIMESTAMP( notif, RP_TAGS_TIMESTAMP, rpal_time_getGlobal() ) )
                {
                    if( NULL != pFileHash )
                    {
                        rSequence_addBUFFER( notif, RP_TAGS_HASH, (RPU8)pFileHash, sizeof( *pFileHash ) );
                    }

                    if( NULL != ( wPath = rpal_string_atow( name ) ) )
                    {
                        cleanPath = rpal_file_cleanw( wPath );

                        if( libOs_getSignature( cleanPath ? cleanPath : wPath, 
                                                &sig, 
                                                OSLIB_SIGNCHECK_NO_NETWORK, 
                                                NULL, 
                                                NULL, 
                                                NULL ) )
                        {
                            if( !rSequence_addSEQUENCE( notif, RP_TAGS_SIGNATURE, sig ) )
                            {
                                rSequence_free( sig );
                            }
                        }

                        if( NULL != cleanPath )
                        {
                            rpal_memory_free( cleanPath );
                        }

                        rpal_memory_free( wPath );
                    }

                    notifications_publish( RP_TAGS_NOTIFICATION_CODE_IDENTITY, notif );
                }
                rSequence_free( notif );
            }
        }
        else
        {
            rMutex_unlock( g_mutex );
        }
    }
}