• About TV's server
  • About the source code
  • About protocols/plugins
  • TV's server API
  • Protocol Initialisation
  • Protocol binding
  • Protocol Listening and handling
  • Protocol data storage
  • Control panel
  • Multi column list
  • Time and date
  • Http protocol functions
  • Protocol initialization

    index About protocols/plugins
    The possibility to expand TV's server with your own protocol was introduced in version 1.06. This implementation was called 'add-in' and was quite basic and limited. TV's server was always using a TCP/IP connection and every add-in had to respond on every incomming connection on every port TV's server was listening on. Certain protocols (like FTP and SMTP) where unable to implement due some limitations and TV's server didn't provided any services (like data storage, TCP/IP and restore functionality). To avoid all these limitations I dediced to restart and rewrite the full project, this time with a good desgin. TV's server should provide the folowing functionality: To be able to know which protocols are able to work together it was necessary to define three different types: One plugin (a DLL-file) is able to provide more than one protocol, these protocols need to be identified somehow. At first, when the plugin gets loaded in to the memory, this is done by a list of names of protocols this plugin provides. TV's server will create an unique number (ProtocolSession) for every protocolname and will pass this number while requesting the plugin to load the protocol. On succes, this unique number will be able to used for the services (like data storage) TV's server has to offer.

    Requirements
    The plugin should at least export the functions GetProtocolNames and StartProtocol.

    GetProtocolNames function
    The function GetProtocolNames needs to pass a list of names of protocols this plugin is providing.

    StartProtocol function
    The function StartProtocol needs to initiate the protocol. A ProtocolSession, uniqe for every protocol, will be created and will be passed through this function, therefore it will be called for every protocol separately. This function should at least make a call to the following functions: Optionally it can call function (before calling the AcceptProtocol function): Passing protocol names example
    This example will show how to implement the function GetProtocolNames. This example exports the protocol TCPIP and HTTP. Below there is an example of function StartProtocol.
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "tvsserver_base.h"
    #include "tvs_redefine_api_names.h"
    
    int GetNullTerminatedListSize(char* list);
    
    char* supportedProtocols = "C_HTTP\0C_TCPIP\0\0";
    
    /*This function should return a list of protocols it supports*/
    int __stdcall GetProtocolNames(char *buf, int *len) {
    	int size;
    
    	if(len==0) return 0;
    
    	size = GetNullTerminatedListSize(supportedProtocols); //calculate the size of the list
    
    	//if the buffer is not big enough, return the size that is needed
    	if(*len==0 || *len<size) {
    		*len = size;
    			return 1;
    	}
    
    	//copy the list to the buffer and return
    	memcpy(buf, supportedProtocols, size);
    	return 1;
    }
    
    /*This function will calculate the size, in bytes, of a null-terminated list*/
    int GetNullTerminatedListSize(char* list) {
    	int cnt=0;
    	int len;
    
    	if(list==0) return 0;
    	
    	while(*list!=0) { //The terminating null byte is found, this is the end of the list
    		len = strlen(list);
    		list += len + 1; //add the size of the item +1 to get the next item
    		cnt += len + 1;
    	}
    	cnt++; //add the last zero byte
    
    	return cnt; //return the size of the list
    }
    
    Start protocol example
    The next example will show how to implement the function StartProtocol.
    #include <stdio.h>
    #include <string.h>
    #include <winsock2.h>
    #include "tvsserver_base.h"
    #include "tvs_redefine_api_names.h"
    #include <stdlib.h>
    
    // Need to link with Ws2_32.lib
    #pragma comment(lib, "ws2_32.lib")
    
    #define HTTP_AMMOUNT_OF_SUB_OPTIONS 1
    #define TCPIP_AMMOUNT_OF_SUB_OPTIONS 2
    
    typedef struct MyTCPIPstruct{
    	int socket;
    } MyTCPIPstruct;
    
    typedef struct MyHTTPstruct{
    	char* serverPath;
    } MyHTTPstruct;
    
    ProtocolSession *http_psess;
    ProtocolSession *tcpip_psess;
    
    #define OPT_TCP_PORT 1
    #define OPT_TCP_VERSION6 2
    
    #define HTTP_AMMOUNT_OF_SUPPPORTED_COMMANDS 3
    char* httpSupportedCommands[HTTP_AMMOUNT_OF_SUPPPORTED_COMMANDS] = {"GET", "POST", "HEAD"};
    
    char* createProtocolSignatureFromArray(char* p_array[], int ammountItems);
    
    int __stdcall StartProtocol(ProtocolSession *psess) {
    	ControlPanelOptions *cpo;
    	WORD wVersionRequested;
    	WSADATA wsaData;
    	int retv=0;
    	char* tempProtocolSignatureStructure=0;
    
    	/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    	wVersionRequested = MAKEWORD(2, 2);
    
    	if(!strcmp(psess->protocolName, "C_HTTP")) { //init the HTTP protocol
    		http_psess = psess;
    
    		tempProtocolSignatureStructure = createProtocolSignatureFromArray(httpSupportedCommands, HTTP_AMMOUNT_OF_SUPPPORTED_COMMANDS);
    
    		//Set the protocol type to PT_LABORER, make room for the MyHTTPstruct structure in the ListenSession and ClientSession and pass the supported API version.
    		if(!SetProtocolInformation(
    			psess, 
    			PT_LABORER | PT_SUPPORT_PSIGN,
    			(ProtocolSignature*) tempProtocolSignatureStructure,
    			sizeof(MyHTTPstruct),
    			V_TV_SERVER_1)
    		)
    			return false;
    
    		//Create a ControlPanelOptions strcuture with room for 1 suboption.
    		cpo = (ControlPanelOptions*) malloc(
    			sizeof(ControlPanelOptions) + 
    			sizeof(ControlPanelOption)*HTTP_AMMOUNT_OF_SUB_OPTIONS // +1 subOption
    		);
    
    		/*Empty memory and fill in the size, in bytes*/
    		memset(cpo, 0, sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*HTTP_AMMOUNT_OF_SUB_OPTIONS);
    		cpo->size = sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*HTTP_AMMOUNT_OF_SUB_OPTIONS;
    
    		//The name of this protocol, communicated to the user, will be "HTTP-plugin"
    		cpo->main.size = sizeof(ControlPanelOption);
    		cpo->main.optionName  = "HTTP-C-plugin"; //the name of this protocol
    
    		//suboption number 1
    		cpo->subOptions[0].size = sizeof(ControlPanelOption);
    		cpo->subOptions[0].name = "opt1"; //identifier name used by the function GetPluginData and SavePluginData
    		cpo->subOptions[0].optionName = "Server-path"; //label of this option
    		cpo->subOptions[0].userValue = (UserValueOption*) malloc(sizeof(UserValueOption));
    		cpo->subOptions[0].userValue->type = TD_ASCII_STRING; //the pointer on defaultValue is a ascii string.
    		cpo->subOptions[0].userValue->defaultValue = "c:\\WWWroot\\"; //default value of this option
    		cpo->subOptions[0].userValue->defaultValueSize = strlen((char*) cpo->subOptions[0].userValue->defaultValue); 
    		cpo->subOptions[0].flags = CPO_GEN_LISTENSESSION_OPTION; //a valid option for the listensession
    
    		SetControlPanelOptions(psess, cpo);
    
    		retv = AcceptProtocol(psess); //retv=false the protocol is rejected, retv=true the protocol is loaded
    
    		free(cpo->subOptions[0].userValue);
    		free(cpo);
    
    	} else if(!strcmp(psess->protocolName, "C_TCPIP")) { //init the TCP/IP protocol
    		tcpip_psess = psess;
    
    		if(!SetProtocolInformation(
    			psess, //ProtocolSession
    			PT_RECEIVER, //Set the type to receiver protocol
    			0, //No Protcol signature for for a PT_RECEIVER protocol
    			sizeof(MyTCPIPstruct), //In every ListenSession and ClientSession I want enough room in the data member for my structure
    			V_TV_SERVER_1) //This protocol supports API version 1
    		)
    			return false;
    
    		//Create a ControlPanelOptions strcuture with room for 1 suboption.
    		cpo = (ControlPanelOptions*) malloc(
    			sizeof(ControlPanelOptions) + 
    			sizeof(ControlPanelOption)*TCPIP_AMMOUNT_OF_SUB_OPTIONS // +2 subOption
    		);
    
    		/*Empty memory and fill in the size, in bytes*/
    		memset(cpo, 0, sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*TCPIP_AMMOUNT_OF_SUB_OPTIONS);
    		cpo->size = sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*TCPIP_AMMOUNT_OF_SUB_OPTIONS;
    
    		//The name of this protocol, communicated to the user, will be "HTTP-plugin"
    		cpo->main.size = sizeof(ControlPanelOption);
    		cpo->main.optionName  = "TCP/IP-C-plugin"; //the name of this protocol
    
    		//suboption number 1
    		cpo->subOptions[0].size = sizeof(ControlPanelOption);
    		cpo->subOptions[0].name = (char*) OPT_TCP_PORT; //identifier integer, instead of a name used by the function GetPluginData and SavePluginData
    		cpo->subOptions[0].optionName = "Port"; //label of this option
    		cpo->subOptions[0].userValue = (UserValueOption*) malloc(sizeof(UserValueOption));
    		cpo->subOptions[0].userValue->type = TD_SHORT_INT; //the defaultValue is an integer
    		cpo->subOptions[0].userValue->defaultValue = (void*) 80; //default value of this option
    		cpo->subOptions[0].userValue->defaultValueSize = 0; 
    		cpo->subOptions[0].flags = CPO_GEN_LISTENSESSION_OPTION | GDPD_NAME_IS_INTEGER | CPO_GEN_LISTENSESSION_READONLY; //a valid option for the listensession, member name is an integer and cannot be modified on a running service
    
    		//suboption number 2
    		cpo->subOptions[1].size = sizeof(ControlPanelOption);
    		cpo->subOptions[1].name = (char*) OPT_TCP_VERSION6; //identifier integer, instead of a name used by the function GetPluginData and SavePluginData
    		cpo->subOptions[1].optionName = "Use version6"; //label of this option
    		cpo->subOptions[1].userValue = (UserValueOption*) malloc(sizeof(UserValueOption));
    		cpo->subOptions[1].userValue->type = TD_BOOLEAN; //the defaultValue is an boolean
    		cpo->subOptions[1].userValue->defaultValue = (void*) 0; //default value of this option is false
    		cpo->subOptions[1].userValue->defaultValueSize = 0; 
    		cpo->subOptions[0].flags = CPO_GEN_LISTENSESSION_OPTION | GDPD_NAME_IS_INTEGER | CPO_GEN_LISTENSESSION_READONLY; //a valid option for the listensession, member name is an integer and cannot be modified on a running service
    
    		SetControlPanelOptions(psess, cpo);
    
    		//Startup the Socket stack
    		if(WSAStartup(wVersionRequested, &wsaData))
    			return 0;
    
    		//We will not make a call to SetControlPanelOptions and immediately call AcceptProtocol
    		retv = AcceptProtocol(psess); //retv=false the protocol is rejected, retv=true the protocol is loaded
    
    		free(cpo->subOptions[0].userValue);
    		free(cpo);
    	}
    
    	//Free the ProtocolSignatureStructure if allocated
    	if(tempProtocolSignatureStructure)
    		free(tempProtocolSignatureStructure); 
    
    	return retv;
    }
    
    char* createProtocolSignatureFromArray(char* p_array[], int ammountItems) {
    	int bufSize, i, tmpLen;
    	char* tmpBuf, *retBuf;
    
    	//Calculate the size of every integer that will contain the size of the command (that last one is zero)
    	bufSize = (ammountItems + 1) * sizeof(int);
    
    	//Calculate the size of the buf members for the ProtocolSignature structure
    	for(i=0; i<ammountItems; i++) {
    		bufSize += strlen(p_array[i]);
    	}
    
    	//Allocate size for the ProtocolSignature structure
    	retBuf = (char*) malloc(bufSize);
    
    	tmpBuf = retBuf;
    	//Copy every item with there size to the array
    	for(i=0; i<ammountItems; i++) {
    		tmpLen = strlen(p_array[i]);
    		*(int*)tmpBuf = tmpLen;
    		tmpBuf += sizeof(int);
    		strcpy(tmpBuf, p_array[i]);
    		tmpBuf += tmpLen;
    	}
    
    	*(int*)tmpBuf = 0;
    
    	return retBuf;
    }