// ===================================================
// Листинг 4. Функции для программы SLIST.CPP
//
// Файл slist!\sap.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <mem.h>
#include <string.h>
#include <dos.h>
#include "sap.hpp"

// ====================================================
// Конструктор класса SLIST
// ====================================================

SLIST::SLIST(int ServiceType) {

// Проверяем наличие сетевой оболочки и
// определяем ее версию

	MajorVersion = 0;

	asm push si
	GetNetWareShellVersion(&MajorVersion,
			&MinorVersion, &Revision);
	asm pop si

// Если оболочка не загружена, завершаем работу
// программы с сообщением об ошибке

	if(MajorVersion == 0) {
		printf("\nОболочка NetWare не загружена\n");
		errno = 0xff;
		return;
	}

// Проверяем тип SAP-запроса

	if (ServiceType != 1 && ServiceType != 3)	{
		errno = NOT_SUPPORTED;
		return;
	}

// Запоминаем атип запроса

	QueryType = ServiceType;

// Инициализируем драйвер протокола IPX

	IPXInitialize();

// Открываем короткоживущий динамический сокет

	SrcSocket = 0x00;
	errno = IPXOpenSocket(&SrcSocket, SHORT_LIVED);

// Заполняем таблицу имен серверов нулями

	memset(ServerName,0,sizeof(ServerName));

// Подготавливаем блоки ECB для приема
// пакетов от SAP-протокола

	for(int i=0;i<MAX_SERVERS;i++) {

// Заполняем блок ECB

		 ReceiveSAPPacket(&Query[i]);

// Ставим в очередь на прием пакета

		 IPXListenForPacket(&Query[i].theECB);
	}

// Если не было ошибок, посылаем запрос

	if (!errno) {
		SendSAPPacket();

// Ждем примерно одну секунду

		sleep(1);

// Переписываем имена серверов и другую
// информацию

		GetServersName();
		GetServersInfo();
	}
}

// ====================================================
// Деструктор класса SLIST
// ====================================================

SLIST::~SLIST() {

// Отменяем ожидающие блоки ECB

	 for(int i=0;i<MAX_SERVERS;i++) {
		 IPXCancelEvent(&Query[i].theECB);
	 }

// Закрываем сокет

	 IPXCloseSocket(SrcSocket);
}

// ====================================================
// Посылка SAP-запроса
// ====================================================

void SLIST::SendSAPPacket(void) {

// Сбрасываем поле inUseFlag и ESRAddress,
// устанавливаем тип пакета 0

	SendPacket.theECB.inUseFlag = 0;
	SendPacket.theECB.ESRAddress = 0;
	SendPacket.SAPq.packetType = 0;

// SAP-пакет состоит из одного фрагмента.
// Записываем в ECB количество фрагментов,
// адрес и размер буфера

	SendPacket.theECB.fragmentCount = 1;
	SendPacket.theECB.fragmentDescriptor[0].address =
			&SendPacket.SAPq;
	SendPacket.theECB.fragmentDescriptor[0].size =
			sizeof(SAPQueryPacket);

// Записываем в ECB номер своего сокета

	SendPacket.theECB.socketNumber = SrcSocket;

// Устанавливаем адрес назначения - все станции
// в текущей сети, сокет SAP_SOCKET.
// Устанавливаем поле непосредственного адреса

	memset(SendPacket.SAPq.destination.network, '\x00', 4);
	memset(SendPacket.SAPq.destination.node, '\xFF', 6);
	SendPacket.SAPq.destination.socket = IntSwap(SAP_SOCKET);
	memset(SendPacket.theECB.immediateAddress, '\xFF', 6);

// Устанавливаем свой адрес в заголовке запроса

	IPXGetInternetworkAddress(SendPacket.SAPq.source.network);
	SendPacket.SAPq.source.socket = IntSwap(SrcSocket);

// Заполняем передаваемый пакет. Устанавливаем тип запроса
// и тип сервера

	SendPacket.SAPq.queryType = IntSwap(QueryType);
	SendPacket.SAPq.serverType = IntSwap(0x0004);

// Посылаем SAP-пакет

	IPXSendPacket(&SendPacket.theECB);

// Ожидаем завершения процесса передачи пакета

	while (SendPacket.theECB.inUseFlag)
		 IPXRelinquishControl();

// Сохраняем код возврата

	errno = SendPacket.theECB.completionCode;
}

// ====================================================
// Прием SAP-пакетов
// ====================================================

void  SLIST::ReceiveSAPPacket(RECEIVE_PACKET	*Query) {

// Сбрасываем поле inUseFlag и ESRAddress

	Query->theECB.inUseFlag = 0;
	Query->theECB.ESRAddress = 0;

// Записываем в ECB количество фрагментов,
// адрес и размер буфера

	Query->theECB.fragmentCount = 1;
	Query->theECB.fragmentDescriptor[0].address =
				&Query->SB;
	Query->theECB.fragmentDescriptor[0].size =
				sizeof(Query->SB);

// Устанавливаем в ECB свой номер сокета

	Query->theECB.socketNumber = SrcSocket;
}

// ====================================================
// Процедура переписывает имена серверов из тех
// блоков ECB, для которых пришли пакеты
// ====================================================

void  SLIST::GetServersName(void) {

	for(int i=0,j=0; i<MAX_SERVERS; i++) {
		if(!Query[i].theECB.inUseFlag) {
			strcpy(ServerName[j],Query[i].SB.ServerName);
			j++;
		}
	}
}

// ====================================================
// Процедура получает информацию о серверах
// ====================================================

void  SLIST::GetServersInfo(void) {

// Получаем номера каналов первичного сервера
// и сервера по умолчанию

	PrimaryConnID   = GetPrimaryConnectionID();
	DefaultConnID   = GetDefaultConnectionID();

// Цикл по всем обнаруженным в сети активным серверам

	for(int i=0; i<MAX_SERVERS; i++) {
		if(ServerName[i][0]) {

// Получаем номер канала сервера

			errno = GetConnectionID(ServerName[i], &ConnID[i]);

// Если канала нет, создаем его, подключаясь к серверу

			if(errno) {
				AttachToFileServer(ServerName[i], &ConnID[i]);
			}

// Делаем текущий сервер предпочтительным, так как
// именно к нему должны поступать запросы

			errno = SetPreferredConnectionID(ConnID[i]);

// Получаем информацию о текущем сервере

			if(!errno)
				errno = GetServerInformation(sizeof(ServerInfo[i]),
								&ServerInfo[i]);

// Получаем серийный номер и номер приложения

			SerialNumber[i]=ApplicationNumber[i]=0L;
			errno = GetNetworkSerialNumber(&SerialNumber[i],
			 &ApplicationNumber[i]);
			errno = 0;
		}
	}
}

// ====================================================
// Процедура распечатывает имена и другую
// информацию о серверах
// ====================================================

void  SLIST::PrintServersName(void) {

// Цикл по всем обнаруженным в сети активным серверам

	for(int i=0; i<MAX_SERVERS; i++) {
		if(ServerName[i][0]) {

// Выводим имя сервера

			printf("%s",ServerInfo[i].serverName);

// Если номер канала текущего сервера совпадает с
// номером канала первичного сервера, выводим
// строку "\t[Primary]"

			if(ConnID[i] == PrimaryConnID)
				printf("\t[Primary]");
			else
				printf("\t[       ]");

// Если номер канала текущего сервера совпадает с
// номером канала сервера по умолчанию, выводим
// строку " [Default]"

			if(ConnID[i] == DefaultConnID)
				printf(" [Default]");
			else
				printf(" [       ]");

// Выводим версию сетевой операционной системы,
// работающей на текущем сервере

			printf(" v.%d.%d, ",
				ServerInfo[i].netwareVersion,
				ServerInfo[i].netwareSubVersion);

// Для подключенных серверов выводим серийный
// номер и номер приложения

			if(SerialNumber[i] != 0L)
				printf("s/n %08.8lX/%04.4X",
					SerialNumber[i], ApplicationNumber[i]);
			else
				printf("- Not Logged In -");

// Выводим номер канала, используемого для связи
// с текущим сервером

			printf("\tConnID: %d,",ConnID[i]);

// Выводим максимальное число каналов, поддерживаемых
// сервером, и количество используемых каналов

			printf(" (%d-%d)\n",
				ServerInfo[i].maxConnectionsSupported,
				ServerInfo[i].connectionsInUse);
		}
	}
}

