Example #1
0
void regexp_remove( char *channelRegexp, char *contentRegexp )
{
    LinkedListItem_t   *item;
    Regexp_t           *regexp;

    if( !channelRegexp ) {
        channelRegexp = channelsAll;
    }

    LinkedListLock( regexpList );

    for( item = regexpList->head; item; item = item->next ) {
        regexp = (Regexp_t *)item;

        if( !strcmp( channelRegexp, regexp->channelRegexp ) &&
            !strcmp( contentRegexp, regexp->contentRegexp ) ) {
            LinkedListRemove( regexpList, item, LOCKED );

            free( regexp->reChannel );
            free( regexp->peChannel );
            free( regexp->reContent );
            free( regexp->peContent );
            free( item );
            LinkedListUnlock( regexpList );
            return;
        }
    }

    LinkedListUnlock( regexpList );
}
Example #2
0
void serverUnvisit( BalancedBTreeItem_t *node )
{
    IRCServer_t        *server;
    IRCChannel_t       *channel;
    LinkedListItem_t   *item;

    if( !node ) {
        return;
    }

    serverUnvisit( node->left );

    server = (IRCServer_t *)node->item;
    server->visited = FALSE;

    if( server->channels ) {
        LinkedListLock( server->channels );

        for( item = server->channels->head; item; item = item->next ) {
            channel = (IRCChannel_t *)item;
            channel->visited = FALSE;
        }
        LinkedListUnlock( server->channels );
    }

    serverUnvisit( node->right );
}
Example #3
0
void ProcOnDisconnected(BN_PInfo I, const char Msg[])
{
    LinkedListItem_t   *item;
    IRCServer_t        *server;
    IRCChannel_t       *channel;

    server = (IRCServer_t *)I->User;

    if( verbose ) {
        LogPrint( LOG_DEBUG, "Event Disconnected : (%s)", Msg);
    }

    if( GlobalAbort ) {
        if( server->channels ) {
            LinkedListLock( server->channels );
            for( item = server->channels->head; item ; item = item->next ) {
                channel = (IRCChannel_t *)item;
                if( channel->enabled ) {
                    db_nick_history( channel, "", HIST_END );
                }
            }
            LinkedListUnlock( server->channels );
        }
        LogPrint( LOG_NOTICE, "Killing thread for %s@%s:%d", server->nick, 
                  server->server, server->port );

        pthread_exit( NULL );
    }
}
Example #4
0
void ProcOnJoinChannel(BN_PInfo I, const char Chan[])
{
    bool                found;
    LinkedListItem_t   *item;
    IRCServer_t        *server;
    IRCChannel_t       *channel;

    server = (IRCServer_t *)I->User;
    LogPrint( LOG_NOTICE, "Joined channel %s on server %s", Chan, 
              server->server);

    if( server->channels ) {
        LinkedListLock( server->channels );
        for( found = false, item = server->channels->head; 
             item && !found; item = item->next ) {
            channel = (IRCChannel_t *)item;
            if( channel->joined || !channel->enabled ) {
                continue;
            }

            if( !strcasecmp(Chan, channel->channel) ) {
                channel->joined = TRUE;
                channel->newChannel = FALSE;
                db_flush_nicks( channel );
                continue;
            }

            transmitMsg( server, TX_JOIN, channel->channel, NULL );
            found = true;
        }
        LinkedListUnlock( server->channels );
    }
}
Example #5
0
void ProcOnRegistered(BN_PInfo I)
{
    bool                found;
    LinkedListItem_t   *item;
    IRCServer_t        *server;
    IRCChannel_t       *channel;

    server = (IRCServer_t *)I->User;

    if( verbose ) {
        LogPrintNoArg( LOG_DEBUG, "Event Registered");
    }

    if( strcmp(server->nickserv, "") ) {
        /* We need to register with nickserv */
        transmitMsg( server, TX_PRIVMSG, server->nickserv, 
                     server->nickservmsg );
    }

    if( server->channels ) {
        LinkedListLock( server->channels );
        for( found = false, item = server->channels->head; 
             item && !found; item = item->next ) {
            channel = (IRCChannel_t *)item;
            if( channel->joined || !channel->enabled ) {
                continue;
            }

            transmitMsg( server, TX_JOIN, channel->channel, NULL );
            found = true;
        }
        LinkedListUnlock( server->channels );
    }
}
Example #6
0
void regexp_parse( IRCServer_t *server, IRCChannel_t *channel, char *who, 
                   char *msg, IRCMsgType_t type )
{
    LinkedListItem_t   *item;
    Regexp_t           *regexp;
    int                 rc;
    int                 lenMsg;
    int                 lenChan;
    int                 ovector[30];
    int                 start;

    if( !channel ) {
        return;
    }

    lenMsg  = strlen( msg );
    lenChan = strlen( channel->fullspec );

    LinkedListLock( regexpList );

    for( item = regexpList->head; item; item = item->next ) {
        regexp = (Regexp_t *)item;

        rc = pcre_exec( regexp->reChannel, regexp->peChannel,
                        channel->fullspec, lenChan, 0, 0, ovector, 30 );
        if( rc < 0 ) {
            /* Channels don't match */
            continue;
        }

        for( rc = 1, start = 0; rc > 0; start = ovector[1] ) {
            rc = pcre_exec( regexp->reContent, regexp->peContent, msg, lenMsg,
                            start, 0, ovector, 30 );
            if( rc >= 0 ) {
                /* We got a channel and content match, call the function */
                regexp->func( server, channel, who, msg, type, ovector, rc,
                              regexp->tag );
            }
        }
    }

    LinkedListUnlock( regexpList );
}
Example #7
0
void QueueKillAll( void )
{
    LinkedListItem_t *listItem;
    QueueObject_t  *queue;

    LinkedListLock(QueueList);
    for (listItem = QueueList->head; listItem; listItem = listItem->next) {
        queue = (QueueObject_t *) listItem;

        /* We don't want to flush the log messages */
        if( queue == LoggingQ ) {
            continue;
        }

        /*
         * To allow all listeners to wake up and hear the GlobalAbort,
         * signal both the cNotEmpty and the cNotFull to all 
         */
        pthread_cond_broadcast(queue->cNotEmpty);
        pthread_cond_broadcast(queue->cNotFull);
    }
    LinkedListUnlock(QueueList);
}
Example #8
0
bool LogFileRemove( char *filename )
{
    LogFileChain_t *logfile;
    LinkedListItem_t *listItem;
    bool found;

    if( filename == NULL )
    {
        return( FALSE );
    }

    LinkedListLock( LogList );

    for( listItem = LogList->head, found = FALSE; 
         listItem != NULL;
         listItem = listItem->next )
    {
        logfile = (LogFileChain_t *)listItem;
        if( logfile->type == LT_FILE && 
            strcmp( filename, logfile->identifier.filename ) == 0 )
        {
            LogOutputRemove( logfile );
            LogPrint( LOG_INFO, "Removed log file: %s", filename );
            found = TRUE;
            /* Take an early exit from the loop */
            break;
        }
    }

    if( found == FALSE )
    {
        LogPrint( LOG_UNKNOWN, "Can't find log file: %s", filename );
    }

    LinkedListUnlock( LogList );
    return( found );
}
Example #9
0
/**
 * @brief Handles all network connections
 * @param arg a pointer to a structure containing port number and timeout
 * @return never returns until shutdown
 *
 * @todo be sure this handles linkdead without removing all player structures
 *       so the player can log back in and be where they were
 *
 * Brings up a TCP listener on the MUD's assigned port and accepts connections.
 * Also reads all data from connected clients and hands the data off to the
 * Input thread.  When there is output to be sent, this thread handles writing
 * to the sockets after the first write has completed.  This thread also
 * handles the disconnection of clients gracefully.
 */
void *ConnectionThread( void *arg )
{
    connectThreadArgs_t    *argStruct;
    int                     portNum;
    char                   *port;
    struct sockaddr_in6     sa;
    int                     count;
    int                     fdCount;
    int                     newFd;
    socklen_t               salen;
    struct timeval          timeout;
    ConnectionItem_t       *item;
    PlayerStruct_t         *player;
    ConnInputItem_t        *connItem;
    ConnDnsItem_t          *dnsItem;
    uint32                  i;
    int                     on;
    int                     retval;
    char                    ch;

    argStruct = (connectThreadArgs_t *)arg;
    portNum = argStruct->port;

    pthread_mutex_lock( startupMutex );

    if( portNum == -1 ) {
        port = pb_get_setting( "listenPort" );
        if( !port ) {
            portNum = 4000;
        } else {
            portNum = atoi(port);
            memfree(port);
        }
    }

    /*
     * Start listening
     */
    listenFd = socket( AF_INET6, SOCK_STREAM, 0 );
    if( listenFd < 0 ) {
        perror("Opening listener socket");
        exit(1);
    }

    on = 1;
    if( setsockopt( listenFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) ) {
        perror("Setting socket to reuse");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sin6_family = AF_INET6;
    sa.sin6_port = htons(portNum);
    sa.sin6_addr = in6addr_any;

    if (bind(listenFd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        perror("Binding listener socket");
        close(listenFd);
        exit(1);
    }

    if (listen(listenFd, 10)) {
        perror("Listening to socket");
        close(listenFd);
        exit(1);
    }

    FD_ZERO(&saveReadFds);
    FD_ZERO(&saveWriteFds);
    FD_ZERO(&saveExceptFds);

    connAddFd(listenFd, &saveReadFds);

    ConnectionList = LinkedListCreate(NULL);

    LogPrint( LOG_NOTICE, "Listening on port %d", portNum );
    pthread_mutex_unlock( startupMutex );


    while( !GlobalAbort ) {
        /*
         * Select on connected and listener
         */
        readFds   = saveReadFds;
        writeFds  = saveWriteFds;
        exceptFds = saveExceptFds;
        timeout.tv_sec  = argStruct->timeout_sec;
        timeout.tv_usec = argStruct->timeout_usec;

        fdCount = select(maxFd+1, &readFds, &writeFds, &exceptFds, &timeout);


        if( GlobalAbort ) {
            continue;
        }

        recalcMaxFd = FALSE;

        /*
         * Open a connection for listener
         */
        if( FD_ISSET(listenFd, &readFds) ) {
            salen = sizeof(struct sockaddr_in6);
            newFd = accept(listenFd, (struct sockaddr *)&sa, &salen);

            connAddFd(newFd, &saveReadFds);
            connAddFd(newFd, &saveExceptFds);

            item = CREATE(ConnectionItem_t);
            if( !item ) {
                /*
                 * No memory!
                 */
                LogPrintNoArg( LOG_EMERG, "Out of memory!" );
                close(newFd);
            } else {
                item->fd = newFd;
                item->buffer = BufferCreate(MAX_BUFSIZE);

                item->hostName = ProtectedDataCreate();
                ProtectedDataLock( item->hostName );
                item->hostName->data = CREATEN(char, 50);
                inet_ntop(AF_INET6, &sa.sin6_addr, item->hostName->data, 50);
                if( !strncmp(item->hostName->data, "::ffff:", 7) ) {
                    bcopy((char *)item->hostName->data + 7,
                          item->hostName->data, 43);
                }
                ProtectedDataUnlock( item->hostName );

                if (!IS_SET(SystemFlags, SYS_SKIPDNS)) {
                    dnsItem = CREATE(ConnDnsItem_t);
                    if( dnsItem ) {
                        dnsItem->connection = item;
                        memcpy(dnsItem->ipAddr, &sa.sin6_addr, 16);
                        QueueEnqueueItem(ConnectDnsQ, dnsItem);
                    }
                }

                player = CREATE(PlayerStruct_t);
                if( !player ) {
                    /*
                     * No memory!
                     */
                    LogPrintNoArg( LOG_EMERG, "Out of memory!" );
                    BufferDestroy(item->buffer);
                    close(newFd);
                    memfree(item);
                } else {
                    item->player = player;
                    player->connection = item;
                    player->in_buffer = item->buffer;

                    LinkedListAdd( ConnectionList, (LinkedListItem_t *)item,
                                   UNLOCKED, AT_TAIL );
                    /*
                     * Pass the info on to the other threads...
                     */
#ifdef DEBUG_CONNECT
                    LogPrint( LOG_INFO, "New connection: %p", player );
#endif
                    connItem = CREATE(ConnInputItem_t);
                    if( connItem ) {
                        connItem->type = CONN_NEW_CONNECT;
                        connItem->player = player;

                        QueueEnqueueItem(ConnectInputQ, (QueueItem_t)connItem);
                    }
                }
            }

            fdCount--;
        }

        if( fdCount ) {
            LinkedListLock( ConnectionList );

            for( item = (ConnectionItem_t *)(ConnectionList->head);
                    item && fdCount;
                    item = (item ? (ConnectionItem_t *)item->link.next
                            : (ConnectionItem_t *)ConnectionList->head) ) {
                if( FD_ISSET( item->fd, &exceptFds ) ) {
                    /*
                     * This connection's borked, close it, remove it, move on
                     */
                    if( FD_ISSET( item->fd, &readFds ) ) {
                        fdCount--;
                    }

                    if( FD_ISSET( item->fd, &writeFds ) ) {
                        fdCount--;
                    }

                    BufferLock( item->buffer );
                    item = connRemove(item);
                    fdCount--;
                    continue;
                }

                if( item && FD_ISSET( item->fd, &readFds ) ) {
                    /*
                     * This connection has data ready
                     */
                    count = BufferAvailWrite( item->buffer, TRUE );
                    if( !count ) {
                        /*
                         * No buffer space, the buffer's unlocked, move on
                         */
                        LogPrint( LOG_INFO, "No buffer space: %p", item );
                        continue;
                    }

                    /*
                     * The buffer's locked
                     */
                    count = read( item->fd, BufferGetWrite( item->buffer ),
                                  count );
                    if( !count ) {
                        LogPrint( LOG_DEBUG, "EOF on %d", item->fd );
                        /*
                         * We hit EOF, close and remove
                         */
                        if( FD_ISSET( item->fd, &writeFds ) ) {
                            fdCount--;
                        }

                        item = connRemove(item);
                        fdCount--;
                        continue;
                    }

                    BufferWroteBytes( item->buffer, count );
                    BufferUnlock( item->buffer );

                    /*
                     * Tell the input thread
                     */
                    connItem = CREATE(ConnInputItem_t);
                    if( connItem ) {
#ifdef DEBUG_INPUT
                        LogPrint( LOG_INFO, "New data: %p", item->player );
#endif
                        connItem->type = CONN_INPUT_AVAIL;
                        connItem->player = item->player;

                        QueueEnqueueItem(ConnectInputQ, (QueueItem_t)connItem);
                    }
                }

                if( item && FD_ISSET( item->fd, &writeFds ) ) {
                    /*
                     * We have space to output, so write if we have anything
                     * to write
                     */
#ifdef DEBUG_OUTPUT
                    LogPrint( LOG_INFO, "Output sent to: %p", item );
#endif
                    if( item->outBufDesc ) {
                        /* TODO: deal with partial block writes */
                        retval = write( item->fd, item->outBufDesc->buf,
                                        item->outBufDesc->len );
                        memfree( item->outBufDesc->buf );
                        memfree( item->outBufDesc );
                        item->outBufDesc = NULL;
                    }
                    /*
                     * Kick the next output
                     */
                    connKickOutput( item );
                    fdCount--;
                }
            }
            LinkedListUnlock( ConnectionList );
        }

        if( recalcMaxFd ) {
            LinkedListLock( ConnectionList );
            maxFd = listenFd;

            for( item = (ConnectionItem_t *)(ConnectionList->head); item;
                    item = (ConnectionItem_t *)item->link.next ) {
                if( item->fd > maxFd ) {
                    maxFd = item->fd;
                }
            }
            LinkedListUnlock( ConnectionList );
        }
    }
Example #10
0
void *bot_server_thread(void *arg)
{
    BN_TInfo           *Info;
    IRCServer_t        *server;
    IRCChannel_t       *channel;
    LinkedListItem_t   *item;
    sigset_t            sigmsk;

    server = (IRCServer_t *)arg;

    if( !server ) {
        return(NULL);
    }

    sigemptyset( &sigmsk );
    pthread_sigmask( SIG_SETMASK, &sigmsk, NULL );

    Info = &server->ircInfo;

    memset(Info, 0, sizeof(BN_TInfo));
    Info->User = (void *)server;
    Info->CB.OnConnected = ProcOnConnected;
    Info->CB.OnJoinChannel = ProcOnJoinChannel;
    Info->CB.OnRegistered = ProcOnRegistered;
    Info->CB.OnUnknown = ProcOnUnknown;
    Info->CB.OnDisconnected = ProcOnDisconnected;
    Info->CB.OnError = ProcOnError;
    Info->CB.OnNotice = ProcOnNotice;
    Info->CB.OnStatus = ProcOnStatus;
    Info->CB.OnCTCP = ProcOnCTCP;
    Info->CB.OnCTCPReply = ProcOnCTCPReply;
    Info->CB.OnWhois = ProcOnWhois;
    Info->CB.OnMode = ProcOnMode;
    Info->CB.OnModeIs = ProcOnModeIs;
    Info->CB.OnNames = ProcOnNames;
    Info->CB.OnWho = ProcOnWho;
    Info->CB.OnBanList = ProcOnBanList;
    Info->CB.OnList = ProcOnList;
    Info->CB.OnKill = ProcOnKill;
    Info->CB.OnInvite = ProcOnInvite;
    Info->CB.OnTopic = ProcOnTopic;
    Info->CB.OnKick = ProcOnKick;
    Info->CB.OnPrivateTalk = ProcOnPrivateTalk;
    Info->CB.OnAction = ProcOnAction;
    Info->CB.OnChannelTalk = ProcOnChannelTalk;
    Info->CB.OnNick = ProcOnNick;
    Info->CB.OnJoin = ProcOnJoin;
    Info->CB.OnPart = ProcOnPart;
    Info->CB.OnQuit = ProcOnQuit;
    Info->CB.OnExcessFlood = ProcOnExcessFlood;

    LogPrint( LOG_NOTICE, "Connecting to %s:%d as %s...", server->server, 
              server->port, server->nick);

    while (BN_Connect(Info, server->server, server->port, 0) != true)
    {
        LogPrint( LOG_NOTICE, "Disconnected from %s:%d as %s.", server->server, 
                  server->port, server->nick);

        if( GlobalAbort || server->threadAbort ) {
            break;
        }

        sleep(10);

        /* Clear the joined flags so we will rejoin on reconnect */
        if( server->channels ) {
            LinkedListLock( server->channels );
            for( item = server->channels->head; item ; item = item->next ) {
                channel = (IRCChannel_t *)item;
                channel->joined = FALSE;
            }
            LinkedListUnlock( server->channels );
        }

        LogPrint( LOG_NOTICE, "Reconnecting to %s:%d as %s...", server->server,
                  server->port, server->nick);
    }

    if( server->channels ) {
        LinkedListLock( server->channels );
        for( item = server->channels->head; item ; item = item->next ) {
            channel = (IRCChannel_t *)item;
            db_nick_history( channel, "", HIST_END );
        }
        LinkedListUnlock( server->channels );
    }

    LogPrint( LOG_NOTICE, "Exiting thread for %s@%s:%d", server->nick,
                          server->server, server->port );
    return(NULL);
}
Example #11
0
void botSighup( int signum, void *arg )
{
    IRCServer_t            *server;
    LinkedListItem_t       *listItem, *next;
    BalancedBTreeItem_t    *item;
    IRCChannel_t           *channel;
    bool                    newChannel = FALSE;

    server = (IRCServer_t *)arg;
    if( !server ) {
        return;
    }

    /*
     * Check each channel on the server, leave those no longer needed
     */
    if( server->channels ) {
        LinkedListLock( server->channels );
        BalancedBTreeLock( server->channelName );
        BalancedBTreeLock( server->channelNum );

        for( listItem = server->channels->head; listItem; listItem = next ) {
            next = listItem->next;
            channel = (IRCChannel_t *)listItem;

            if( !channel->visited ) {
                channelLeave( server, channel, channel->channel );
                regexpBotCmdRemove( server, channel );
                LinkedListRemove( server->channels, listItem, LOCKED );

                item = BalancedBTreeFind( server->channelName, 
                                          &channel->channel, LOCKED );
                if( item ) {
                    BalancedBTreeRemove( server->channelName, item, LOCKED,
                                         FALSE );
                }

                item = BalancedBTreeFind( server->channelNum,
                                          &channel->channelId, LOCKED );
                if( item ) {
                    BalancedBTreeRemove( server->channelNum, item, LOCKED,
                                         FALSE );
                }

                ThreadAllNotifyChannel( channel );

                cursesMenuItemRemove( 2, MENU_CHANNELS, channel->menuText );
                free( channel->menuText );
                free( channel->channel );
                free( channel->fullspec );
                free( channel->url );
                free( channel );
            } else if( channel->newChannel && channel->enabled && 
                       !channel->joined && !newChannel ) {
                newChannel = TRUE;
                transmitMsg( server, TX_JOIN, channel->channel, NULL );
            } else if( channel->newChannel && !channel->enabled ) {
                channel->newChannel = FALSE;
            }
        }
        BalancedBTreeUnlock( server->channelNum );
        BalancedBTreeUnlock( server->channelName );
        LinkedListUnlock( server->channels );
    }
}
Example #12
0
void LogItemOutput( void *vitem )
{
    LoggingItem_t      *item;
    struct tm           ts;
    char                line[MAX_STRING_LENGTH];
    char                usPart[9];
    char                timestamp[TIMESTAMP_MAX];
    int                 length;
    LinkedListItem_t   *listItem, *next;
    LogFileChain_t     *logFile;
    static char        *unknown = "thread_unknown";
    char               *threadName;

    if( !vitem ) {
        return;
    }

    item = (LoggingItem_t *)vitem;

    localtime_r( (const time_t *)&(item->tv.tv_sec), &ts );
    strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%b-%d %H:%M:%S",
              (const struct tm *)&ts );
    snprintf( usPart, 9, ".%06d ", (int)(item->tv.tv_usec) );
    strcat( timestamp, usPart );
    length = strlen( timestamp );
    
    LinkedListLock( LogList );
    
    for( listItem = LogList->head; listItem; listItem = next ) {
        logFile = (LogFileChain_t *)listItem;
        next = listItem->next;

        switch( logFile->type ) {
        case LT_SYSLOG:
            syslog( item->level, "%s", item->message );
            break;
        case LT_CONSOLE:
            sprintf( line, "%s %s\n", timestamp, item->message );
            LogWrite( logFile, line, strlen(line) );
            break;
        case LT_FILE:
            threadName = thread_name( item->threadId );
            if( !threadName ) {
                threadName = unknown;
            }
            sprintf( line, "%s %s %s:%d (%s) - %s\n", timestamp, threadName,
                     item->file, item->line, item->function, 
                     item->message );
            LogWrite( logFile, line, strlen(line) );
            break;
        case LT_NCURSES:
            sprintf( line, "%s %s\n", timestamp, item->message );
            cursesLogWrite( line );
            break;
        default:
            break;
        }

        if( logFile->aborted ) {
            LogOutputRemove( logFile );
        }
    }

    LinkedListUnlock( LogList );

    free( item->message );
    free( item );
}
Example #13
0
LinkedList_t *pluginFindPlugins( char *prefix, char *extension )
{
    LinkedList_t   *list;
    PluginItem_t   *item;
    struct dirent **namelist;
    int             n;
    int             i;
    int             len;
    PluginSpec_t   *spec;
    char           *name;

    list = LinkedListCreate();
    LinkedListLock( list );

    ProtectedDataLock( extBlock );

    if( extBlock->data ) {
        spec = (PluginSpec_t *)extBlock->data;
        if( spec->prefix ) {
            free( spec->prefix );
        }
        if( spec->extension ) {
            free( spec->extension );
        }
        free( extBlock->data );
    }

    spec = (PluginSpec_t *)malloc(sizeof(PluginSpec_t));
    memset( spec, 0, sizeof(PluginSpec_t) );
    extBlock->data = (void *)spec;

    if( prefix ) {
        spec->prefix    = strdup( prefix );
        spec->prefLen   = strlen( prefix );
    }

    spec->extension = strdup( extension );
    spec->extLen    = strlen( extension );

    n = scandir( PLUGIN_PATH, &namelist, filterFile, alphasort );

    for( i = 0; i < n; i++ ) {
        item = (PluginItem_t *)malloc(sizeof(PluginItem_t));
        name = namelist[i]->d_name;
        len = strlen(name);
        item->plugin = strndup( &name[spec->prefLen],
                                len - spec->prefLen - spec->extLen );
        item->script = strdup( name );
        LinkedListAdd( list, (LinkedListItem_t*)item, LOCKED, AT_TAIL );
        free( namelist[i] );
    }

    ProtectedDataUnlock( extBlock );

    if( n >= 0 ) {
        free( namelist );
    }

    LinkedListUnlock( list );

    return( list );
}