/*
====================================================================
Disconnect from current server if any.
====================================================================
*/
void client_disconnect()
{
#ifdef NETWORK_ENABLED
	char buf[128];
	
	if ( !client_is_connected ) return;
	
	/* disconnect */
	socket_print_stats( &client );
	sprintf( buf, _("disconnected from %s"), 
		 net_addr_to_string(&client.remote_addr) );
	client_add_chatter( buf, 1 );
	buf[0] = MSG_DISCONNECT;
	client_transmit( CODE_BLUE, 1, buf );
	client_is_connected = 0;
	client_data_clear();
	gui_label_set_text( label_channel, "MAIN" );
#endif
}
NSAPI_PUBLIC Session *session_fill(Session *ns) 
{
    pool_handle_t *pool = pool_create();

    ns->pool = pool;
    systhread_setdata(getThreadMallocKey(), ns->pool);

    ns->client = pblock_create(SESSION_HASHSIZE);
    ns->inbuf = netbuf_open(ns->csd, NET_BUFFERSIZE);
    ns->fill = 1;

    char  session_ipaddr[sizeof "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255"];

    if (ns->pr_client_addr) {
        if (ns->pr_client_addr->raw.family == PR_AF_INET || ns->pr_client_addr->raw.family == PR_AF_INET6) {
            net_addr_to_string(ns->pr_client_addr, session_ipaddr, sizeof(session_ipaddr));
            pblock_kvinsert(pb_key_ip, session_ipaddr, strlen(session_ipaddr), ns->client);
        }
    } else {
        net_inet_ntoa(ns->iaddr, session_ipaddr);
        pblock_kvinsert(pb_key_ip, session_ipaddr, strlen(session_ipaddr), ns->client);
    }

    session_fill_cla(pool, ns);

    NSAPISession *nsn = (NSAPISession *)ns;
    nsn->thread_data = NULL;
    nsn->httpfilter = NULL;
    nsn->input_done = PR_FALSE;
    nsn->input_os_pos = 0;
    nsn->exec_rq = NULL;
    nsn->filter_rq = NULL;
    nsn->session_clone = PR_FALSE;
    nsn->received = 0;
    nsn->transmitted = 0;

    INTsession_fill_ssl(ns);
    return ns;
}
int write_stats_dump(PRFileDesc *fd, StatsHeaderNode *hdr)
{
    int i = 0;
    StatsManager::lockStatsData();
    const StatsHeader *hdrStats = &hdr->hdrStats;

    // Version
    PR_fprintf(fd, "\n%s\n", hdrStats->versionServer);

    // Uptime
    char buffer[25];
    PRExplodedTime tm;
    PR_ExplodeTime(hdrStats->timeStarted, PR_LocalTimeParameters, &tm);
    PR_FormatTimeUSEnglish(buffer, sizeof(buffer), "%c", &tm);
    PR_fprintf(fd, "\nServer started %s\n", buffer);
    {
        StatsProcessNode *process = hdr->process;
        while (process) {
            const StatsProcessSlot *procStats = &process->procStats;
            PR_ExplodeTime(procStats->timeStarted, PR_LocalTimeParameters, &tm);
            PR_FormatTimeUSEnglish(buffer, sizeof(buffer), "%c", &tm);
            PR_fprintf(fd, "Process %d started %s\n", procStats->pid, buffer);
            process = process->next;
        }
    }

    // ConnectionQueue
    {
        StatsConnectionQueueSlot connqueues;
        memset(&connqueues, 0, sizeof(connqueues));

        // Accumulate connection queue buckets for every process
        StatsProcessNode *process = hdr->process;
        while (process) {
            StatsConnectionQueueNode *connQueueNode = process->connQueue;
            while (connQueueNode) {
                const StatsConnectionQueueSlot *connqueue = &connQueueNode->connQueueStats;
                connqueues.countQueued += connqueue->countQueued;
                connqueues.peakQueued += connqueue->peakQueued;
                connqueues.maxQueued += connqueue->maxQueued;
                connqueues.countOverflows += connqueue->countOverflows;
                connqueues.countTotalQueued += connqueue->countTotalQueued;
                connqueues.ticksTotalQueued += connqueue->ticksTotalQueued;
                connqueues.countTotalConnections += connqueue->countTotalConnections;
                connqueues.countQueued1MinuteAverage += connqueue->countQueued1MinuteAverage;
                connqueues.countQueued5MinuteAverage += connqueue->countQueued5MinuteAverage;
                connqueues.countQueued15MinuteAverage += connqueue->countQueued15MinuteAverage;
                connQueueNode = connQueueNode->next;
            }
            process = process->next;
        }

        PRFloat64 count = (PRInt64)connqueues.countTotalQueued;
        PRFloat64 ticks = (PRInt64)connqueues.ticksTotalQueued;
        if (count < 1.0)
            count = 1.0;

        PR_fprintf(fd,
                   "\nConnectionQueue:\n"
                   "-----------------------------------------\n"
                   "Current/Peak/Limit Queue Length            %d/%d/%d\n"
                   "Total Connections Queued                   %llu\n"
                   "Average Queue Length (1, 5, 15 minutes)    %4.2f, %4.2f, %4.2f\n"
                   "Average Queueing Delay                     %.2f milliseconds\n",
                   connqueues.countQueued, connqueues.peakQueued, connqueues.maxQueued,
                   connqueues.countTotalQueued,
                   connqueues.countQueued1MinuteAverage,
                   connqueues.countQueued5MinuteAverage,
                   connqueues.countQueued15MinuteAverage,
                   ticks / count / hdrStats->ticksPerSecond * 1000.0);
    }

    // Listen sockets
    {
        StatsProcessNode *process = hdr->process;
        StatsListenNode *lsNode = (process) ? process->ls : 0;
        while (lsNode) {
            StatsListenSlot *ls = &lsNode->lsStats;
            PRNetAddr addr;
            memcpy(&addr, &ls->address, sizeof(ls->address));
            char szAddress[512];
            memset(szAddress, 0, sizeof(szAddress));
            net_addr_to_string(&addr, szAddress, sizeof(szAddress)-1);
            PR_fprintf(fd, 
                       "\nListenSocket %s:\n"
                       "------------------------\n"
                       "Address                   %s://%s:%hu\n"
                       "Acceptor Threads          %d\n"
                       "Default Virtual Server    %s\n",
                       ls->id,
                       ls->flagSecure ? "https" : "http",
                       szAddress,
                       PR_ntohs(ls->address.inet.port),
                       ls->countAcceptors,
                       ls->defaultVsId
                    );
            lsNode = lsNode->next;
        }
    }

    // Keepalive Info
    {
        StatsKeepaliveBucket keepalives;
        memset(&keepalives, 0, sizeof(keepalives));

        // Accumulate keepalive buckets for every process
        StatsProcessNode *process = hdr->process;
        while (process) {
            StatsKeepaliveBucket *procKeepAlive = NULL;
            procKeepAlive = &process->procStats.keepAliveBucket;
            StatsManagerUtil::accumulateKeepAlive(&keepalives, procKeepAlive);
            process = process->next;
        }

        PR_fprintf(fd,
                   "\nKeepAliveInfo:\n"
                   "--------------------\n"
                   "KeepAliveCount        %lu/%lu\n"
                   "KeepAliveHits         %llu\n"
                   "KeepAliveFlushes      %llu\n"
                   "KeepAliveRefusals     %llu\n"
                   "KeepAliveTimeouts     %llu\n"
                   "KeepAliveTimeout      %lu seconds\n",
                   keepalives.countConnections,
                   keepalives.maxConnections,
                   keepalives.countHits,
                   keepalives.countFlushes,
                   keepalives.countRefusals,
                   keepalives.countTimeouts,
                   keepalives.secondsTimeout);
    }

    // Session Creation Info
    {
        PRUint32 countThreads = 0;
        PRUint32 countBusy = 0;
        PRUint32 countKeepAlive = 0;
        PRUint32 countKeepAliveThreads = 0;

        // Count the number of active threads
        StatsProcessNode *process = hdr->process;
        while (process) {
            StatsProcessSlot *procStats = &process->procStats;
            if (procStats->mode != STATS_PROCESS_EMPTY) {
                StatsThreadNode *thread = process->thread;
                while (thread) {
                    const StatsThreadSlot *threadStats = &thread->threadStats;
                    if (threadStats->mode != STATS_THREAD_EMPTY) {
                        countThreads++;
                        if (threadStats->mode == STATS_THREAD_KEEPALIVE) {
                            countKeepAlive++;
                        } else if (threadStats->mode != STATS_THREAD_IDLE) {
                            countBusy++;
                        }
                    }
                    thread = thread->next;
                }
            }
            // Get number of keepalive threads
            StatsKeepaliveBucket *procKeepAlive = NULL;
            procKeepAlive = &process->procStats.keepAliveBucket;
            countKeepAliveThreads += procKeepAlive->numThreads;

            process = process->next;
        }

        PR_fprintf(fd, 
                   "\nSessionCreationInfo:\n"
                   "------------------------\n"
                   "Active Sessions           %lu\n"
                   "Keep-Alive Sessions       %lu\n"
                   "Total Sessions Created    %lu/%lu\n",
                   countBusy,
                   countKeepAlive,
                   countThreads, (hdrStats->maxThreads * hdrStats->maxProcs + countKeepAliveThreads));
    }

    // Cache Info
    {
        StatsCacheBucket caches;
        memset(&caches, 0, sizeof(caches));

        // Accumulate cache buckets for every process
        StatsProcessNode *process = hdr->process;
        while (process) {
            StatsCacheBucket *procCache = NULL;
            procCache = &process->procStats.cacheBucket;
            StatsManagerUtil::accumulateCache(&caches, procCache);
            process = process->next;
        }

        if (caches.flagEnabled) {
            PR_fprintf(fd,
                       "\nCacheInfo:\n"
                       "-----------------------\n");

            PRInt64 fileLookups = caches.countHits + caches.countMisses;

            PR_fprintf(fd,
                       "File Cache Enabled       yes\n"
                       "File Cache Entries       %lu/%lu\n"
                       "File Cache Hit Ratio     %llu/%lld (%6.2f%%)\n"
                       "Maximum Age              %d\n",
                       caches.countEntries,
                       caches.maxEntries,
                       caches.countHits,
                       fileLookups,
                       percent(caches.countHits, fileLookups),
                       caches.secondsMaxAge);

            PRInt64 accelRequests = caches.countAcceleratableRequests + caches.countUnacceleratableRequests;
            PRInt64 accelResponses = caches.countAcceleratableResponses + caches.countUnacceleratableResponses;
            PRInt64 accelLookups = caches.countAcceleratorHits + caches.countAcceleratorMisses;

            PR_fprintf(fd,
                       "Accelerator Entries      %lu/%lu\n"
                       "Acceleratable Requests   %llu/%lld (%6.2f%%)\n"
                       "Acceleratable Responses  %llu/%lld (%6.2f%%)\n"
                       "Accelerator Hit Ratio    %llu/%lld (%6.2f%%)\n",
                       caches.countAcceleratorEntries,
                       caches.maxEntries,
                       caches.countAcceleratableRequests,
                       accelRequests,
                       percent(caches.countAcceleratableRequests, accelRequests),
                       caches.countAcceleratableResponses,
                       accelResponses,
                       percent(caches.countAcceleratableResponses, accelResponses),
                       caches.countAcceleratorHits,
                       accelLookups,
                       percent(caches.countAcceleratorHits, accelLookups));
        } else {
            PR_fprintf(fd, "\nServer cache disabled\n");
        }
    }

    // Thread pool info
    {
        StatsThreadPoolBucket pools[FUNC_MAXPOOLS];
        const char *names[FUNC_MAXPOOLS];
        memset(pools, 0, sizeof(pools));
        memset(names, 0, sizeof(names));

        // Accumulate thread pool buckets for every process
        StatsProcessNode *process = hdr->process;
        while (process) {
            StatsThreadPoolNode *pool = process->threadPool;
            for (i = 0; pool && (i < FUNC_MAXPOOLS); i++) {
                StatsManagerUtil::accumulateThreadPool(&pools[i],
                                                       &pool->threadPoolStats);
                names[i] = STATS_GET_NAME(&pool->threadPoolStats);
                pool = pool->next;
            }
            process = process->next;
        }

        int poolFlag = 0;

        for (i = 0; i < FUNC_MAXPOOLS; i++)
        {
            if (names[i]) {
                if (!poolFlag)
                {
                    poolFlag = 1;
                    PR_fprintf(fd,
                               "\nNative pools:\n"
                               "----------------------------\n");
                }

                PR_fprintf(fd,
                           "%s:\n"
                           "Idle/Peak/Limit               %lu/%lu/%lu\n"
                           "Work Queue Length/Peak/Limit  %lu/%lu/%lu\n",
                           names[i],
                           pools[i].countThreadsIdle, 
                           pools[i].countThreads, 
                           pools[i].maxThreads,
                           pools[i].countQueued, 
                           pools[i].peakQueued, 
                           pools[i].maxQueued);
            }
        }
    }

    // DNS info
    {
        StatsDnsBucket dnss;
        memset(&dnss, 0, sizeof(dnss));

        // Accumulate DNS buckets for every process
        StatsProcessNode *process = hdr->process;
        while (process) {
            StatsManagerUtil::accumulateDNS(&dnss,
                                            &process->procStats.dnsBucket);
            process = process->next;
        }

        if (dnss.flagCacheEnabled) {
            PRUint32 totalhits = dnss.countCacheMisses + dnss.countCacheHits;
            float hitratio;

            if (totalhits > 0.0)
                hitratio = (float)dnss.countCacheHits / (float)totalhits;
            else
                hitratio = 0.0;
            
            PR_fprintf(fd, 
                       "\nDNSCacheInfo:\n"
                       "------------------\n"
                       "enabled             yes\n"
                       "CacheEntries        %lu/%lu\n"
                       "HitRatio            %lu/%lu (%6.2f%%)\n",
                       dnss.countCacheEntries,
                       dnss.maxCacheEntries,
                       dnss.countCacheHits,
                       totalhits,
                       hitratio * 100.0);
        } else {
            PR_fprintf(fd, "\nServer DNS cache disabled\n");
        }

        if (dnss.flagAsyncEnabled) {
            PR_fprintf(fd, 
                       "\nAsyncDNS Data:\n"
                       "------------------\n"
                       "enabled             yes\n"
                       "NameLookups         %lu\n"
                       "AddrLookups         %lu\n"
                       "LookupsInProgress   %lu\n",
                       dnss.countAsyncNameLookups,
                       dnss.countAsyncAddrLookups,
                       dnss.countAsyncLookupsInProgress);
        } else {
            PR_fprintf(fd, "\nAsync DNS disabled\n");
        }
    }

    // NSAPI Profile Info
    if (hdrStats->maxProfileBuckets) {
        int i;

        StatsProfileBucket *profiles = new StatsProfileBucket[hdrStats->maxProfileBuckets];
        const char **names = new const char*[hdrStats->maxProfileBuckets];
        const char **descs = new const char*[hdrStats->maxProfileBuckets];
        memset(profiles, 0, sizeof(profiles[0]) * hdrStats->maxProfileBuckets);
        memset(names, 0, sizeof(names[0]) * hdrStats->maxProfileBuckets);
        memset(descs, 0, sizeof(descs[0]) * hdrStats->maxProfileBuckets);

        // Accumulate profiles for every thread
        StatsProcessNode *process = hdr->process;
        while (process) {
            StatsThreadNode *thread = process->thread;
            while (thread) {
                StatsProfileNode *profile = thread->profile;
                for (i = 0; i < hdrStats->maxProfileBuckets; i++) {
                    StatsManagerUtil::accumulateProfile(&profiles[i],
                                                        &profile->profileStats);
                    names[i] = STATS_GET_NAME(&profile->profileStats);
                    descs[i] = STATS_GET_DESCRIPTION(&profile->profileStats);
                    profile = profile->next;
                }
                thread = thread->next;
            }
            process = process->next;
        }

        PRInt64 tr = profiles[STATS_PROFILE_ALL].countRequests;
        PRInt64 tc = profiles[STATS_PROFILE_ALL].countCalls;
        float td = (float)(PRInt64)profiles[STATS_PROFILE_ALL].ticksDispatch;
        float tf = (float)(PRInt64)profiles[STATS_PROFILE_ALL].ticksFunction;
        td /= hdrStats->ticksPerSecond;
        tf /= hdrStats->ticksPerSecond;
        float tt = td + tf;

        PR_fprintf(fd, 
                   "\nPerformance Counters:\n"
                   "------------------------------------------------\n"
                   "                           Average         Total      Percent\n\n"
                   "Total number of requests:             %10llu\n"
                   "Request processing time:   %7.4f  %12.4f\n",
                   tr,
                   (tr > 0) ? (tt / (float)tr) : 0, tt);

        for (i = hdrStats->maxProfileBuckets-1; i >= STATS_PROFILE_DEFAULT; i--) {
            PRInt64 r = profiles[i].countRequests;
            PRInt64 c = profiles[i].countCalls;
            const char *name = names[i];

            // Don't display cache-bucket if it's empty.  This is a predefined
            // bucket that stored NSAPI accelerator cache statistics in
            // previous versions of the server.
            if (!c && i == STATS_PROFILE_CACHE && !strcmp(name, "cache-bucket")) {
                continue;
            }

            float d = (float)(PRInt64)profiles[i].ticksDispatch;
            float f = (float)(PRInt64)profiles[i].ticksFunction;
            d /= hdrStats->ticksPerSecond;
            f /= hdrStats->ticksPerSecond;
            float t = d + f;

            PR_fprintf(fd, 
                       "\n%s (%s)\n"
                       "Number of Requests:                 %12llu    (%6.2f%%)\n"
                       "Number of Invocations:              %12llu    (%6.2f%%)\n"
                       "Latency:                   %7.4f  %12.4f    (%6.2f%%)\n"
                       "Function Processing Time:  %7.4f  %12.4f    (%6.2f%%)\n"
                       "Total Response Time:       %7.4f  %12.4f    (%6.2f%%)\n",
                       name, SAFESTRING(descs[i]),
                       r, (tr > 0) ? (100.0 * r / (PRFloat64) tr) : 0,
                       c, (tc > 0) ? (100.0 * c / (PRFloat64) tc) : 0,
                       (r > 0)? (d / r): 0, d, (tt > 0)? (100 * d / tt): 0,
                       (r > 0)? (f / r): 0, f, (tt > 0)? (100 * f / tt): 0,
                       (r > 0)? (t / r): 0, t, (tt > 0)? (100 * t / tt): 0);
        }

        delete[] profiles;
        delete[] names;
        delete[] descs;

    } else {
        PR_fprintf(fd, "\nPerformance Statistics disabled\n");
    }

    // Session info
    {
        StatsProcessNode *process;
        int x;
        int y;

        // Count the number of rows in our session table
        int nr = 0;
        for (process = hdr->process; process; process = process->next) {
            StatsThreadNode *thread;
            for (thread = process->thread; thread; thread = thread->next)
                nr++;
        }

        SessionRow *rows = new SessionRow[nr];

        PRTime now = PR_Now();

        // Collect stats from each session
        y = 0;
        for (process = hdr->process; process; process = process->next) {
            StatsThreadNode *thread;
            for (thread = process->thread; thread; thread = thread->next) {
                thread->lock();

                PRUint32 mode = thread->threadStats.mode;
                if (mode != STATS_THREAD_EMPTY && mode != STATS_THREAD_IDLE &&
                    mode != STATS_THREAD_KEEPALIVE) {
                    rows[y].columns[SESSION_COLUMN_PROCESS].printf("%u", process->procStats.pid);
                    rows[y].columns[SESSION_COLUMN_STATUS] = StatsManager::getMode(&thread->threadStats);

                    char addr[NET_ADDR_STRING_SIZE];
                    net_addr_to_string(&thread->threadStats.addressClient, addr, sizeof(addr));
                    rows[y].columns[SESSION_COLUMN_CLIENT] = addr;

                    rows[y].timeRequestStarted = thread->threadStats.timeRequestStarted;
                    if (rows[y].timeRequestStarted != 0) {
                        PRTime microseconds = now - rows[y].timeRequestStarted;
                        int seconds = (microseconds + PR_USEC_PER_SEC/2) / PR_USEC_PER_SEC;
                        rows[y].columns[SESSION_COLUMN_AGE].printf("%d", seconds);
                    }

                    rows[y].columns[SESSION_COLUMN_VS] = thread->threadStats.vsId;
                    rows[y].columns[SESSION_COLUMN_METHOD] = thread->threadStats.requestBucket.method;
                    rows[y].columns[SESSION_COLUMN_URI] = thread->threadStats.requestBucket.uri;
                    rows[y].columns[SESSION_COLUMN_FUNCTION] = STATS_GET_FUNCTION_NAME(&thread->threadStats);

                    y++;
                }

                thread->unlock();
            }
        }

        // We'll only display stats for active sessions
        PR_ASSERT(y <= nr);
        nr = y;

        // Each column is at least as wide as its label
        int widths[SESSION_COLUMN_COUNT];
        for (x = 0; x < SESSION_COLUMN_COUNT; x++)
            widths[x] = session_column_labels[x].length();

        // Ensure each column is wide enough to fit its largest value
        for (y = 0; y < nr; y++) {
            for (x = 0; x < SESSION_COLUMN_COUNT; x++) {
                if (widths[x] < rows[y].columns[x].length())
                    widths[x] = rows[y].columns[x].length();
            }
        }

        // Leave at least 2 spaces between columns
        for (x = 0; x < SESSION_COLUMN_COUNT - 1; x++)
            widths[x] += 2;

        // Calculate total line width and individual column offsets
        int width = 0;
        int offsets[SESSION_COLUMN_COUNT];
        for (x = 0 ; x < SESSION_COLUMN_COUNT; x++) {
            offsets[x] = width;
            width += widths[x];
        }

        char *line = (char *) malloc(width + sizeof('\n'));

        // Output table heading and column labels
        PR_fprintf(fd, "\nSessions:\n");
        memset(line, '-', width);
        line[width] = '\n';
        PR_Write(fd, line, width + sizeof('\n'));
        PR_Write(fd, line, format_session(line, offsets, session_column_labels));
        PR_Write(fd, "\n", 1);

        // Sort rows by age
        const SessionRow **sorted = new const SessionRow *[nr];
        for (y = 0; y < nr; y++)
            sorted[y] = &rows[y];
        qsort(sorted, nr, sizeof(sorted[0]), &session_cmp);

        // Output individual rows
        for (y = 0; y < nr; y++)
            PR_Write(fd, line, format_session(line, offsets, sorted[y]->columns));

        delete [] sorted;

        free(line);

        delete [] rows;
    }

    StatsManager::unlockStatsData();
    return REQ_PROCEED;
}