Code: Select all
#Debug = 1 ; 1 = Debug -> set TrayIcon
#Title = "ServQLite" ; Name for diverse usages
#Version = "0.0.6" ; current version
#ServQL_Path = ""
Global ServQL_Port = 6832 ; Standard-Port for ServQLite
Global BufferSize = 4096 ; Puffer for transfer of text
Global BlobSize = 8196 ; maximal size of a BLOB
Global ClientTimeOut = 10 * 60 * 1000 ; 10 minutes Time-Out for Clients
Global CheckTimeOut = 1 * 60 * 1000 ; Check for Time-Out every 60 seconds
Global LogFile = 0 ; File-ID for logfile
Global SEvent = 0 ; for event-management
Global ClientID = 0 ; for event-management
Global Quit = 0 ; Quit = 1 -> exit Server
CompilerIf #PB_Compiler_Debugger
Global location$ = ""
CompilerElse
Global location$ = Left(GetPathPart(ProgramFilename()),FindString(GetPathPart(ProgramFilename()),GetFilePart(ProgramFilename())+".app",1)-1)
CompilerEndIf
Enumeration
#ServQL_Blob_No
#ServQL_Blob_Upload
#ServQL_Blob_Download
EndEnumeration
Enumeration
#ServQL_OK
#ServQL_DB_Created
#ServQL_DB_Exists
#ServQL_DB_Not_Found
#ServQL_DB_Opened
#ServQL_DB_Creation_Error
#ServQL_DB_Opened_Error
#ServQL_DB_Query_Success
#ServQL_DB_Query_Fault
#ServQL_Last_Row
#ServQL_First_Row
#ServQL_No_Network
#ServQL_Mem_Error
#ServQL_Port_Error
#ServQL_Ready
EndEnumeration
Structure SerQL_Query
ClientID.l
Request$
Cur.l
Max.l
DB.l
LastConnect.l
ClientIP.l
*Blob
Size.l
Richtung.l
Column.l
LastError.l
EndStructure
Global NewList ServQL.serql_query()
Procedure.l FindeLinked(ClientID)
Define Temp
Temp = 0
ForEach ServQL()
If ServQL()\ClientID = ClientID
ProcedureReturn Temp
EndIf
Temp + 1
Next
ProcedureReturn - 1
EndProcedure
Procedure ServQL_Log(Text$)
WriteString(LogFile, "[" + FormatDate("%mm/%dd/%yyyy - %hh:%ii:%ss", Date()) + "]: " + Text$ + #CR$)
EndProcedure
Procedure.s ServQL_DatabaseError(ClientID)
Define Temp$, ServQL_LastError.l
SelectElement(ServQL(), ClientID)
ServQL_LastError = ServQL()\LastError
Temp$ = Str(ServQL_LastError) + ": "
Select ServQL_LastError
Case #ServQL_OK : Temp$ + "no error"
Case #ServQL_DB_Created : Temp$ + "Database created"
Case #ServQL_DB_Exists : Temp$ + "Database exists"
Case #ServQL_DB_Not_Found : Temp$ + "Database not found"
Case #ServQL_DB_Opened : TemP$ + "Database opened"
Case #ServQL_DB_Creation_Error : Temp$ + "Database could not be created"
Case #ServQL_DB_Opened_Error : TemP$ + "Database could not be opened"
Case #ServQL_DB_Query_Success : Temp$ + "Query successfully started"
Case #ServQL_DB_Query_Fault : Temp$ + "Query fault" ; !!
Case #ServQL_Last_Row : Temp$ + "Reached last row in Table"
Case #ServQL_First_Row : Temp$ + "Reached first row in Table"
Case #ServQL_No_Network : Temp$ + "No Network reachable"
Case #ServQL_Mem_Error : Temp$ + "Could not reserve enough memory"
Case #ServQL_Port_Error : Temp$ + "Port already in use. Please change Configuration."
EndSelect
ProcedureReturn Temp$
EndProcedure
Procedure.s ServQL_GetTable(Query$)
Define Ab.l, Bis.l
Ab = FindString(UCase(Query$), " FROM ", 0) + 6
Bis = FindString(Query$ + " ", " ", Ab)
ProcedureReturn Trim(Mid(Query$, Ab));, Bis - Ab))
EndProcedure
Procedure.l ServQL_CreateDatabase(DatabaseName$, User$, Password$) ; - OK
Define DBFile.l
DBFile = ReadFile(#PB_Any, #ServQL_Path + DatabaseName$)
If DBFile <> 0
If IsFile(DBFile)
CloseFile(DBFile)
EndIf
ProcedureReturn #ServQL_DB_Exists
Else
DBFile = CreateFile(#PB_Any, #ServQL_Path + DatabaseName$)
If IsFile(DBFile)
CloseFile(DBFile)
EndIf
If DBFile <> 0
ProcedureReturn #ServQL_DB_Created
Else
ProcedureReturn #ServQL_DB_Creation_Error
EndIf
EndIf
EndProcedure
Procedure.l ServQL_OpenDatabase(DatabaseName$, User$, Password$, ClientID) ; -
; fehlt: Statistik ermitteln (Anzahl Tabellen, Anzahl Datensätze, etc.)
; fehlt: Benutzername und Passwort
Define DBFile.l
SelectElement(ServQL(), ClientID)
DBFile = ReadFile(#PB_Any,#ServQL_Path + DatabaseName$)
If DBFile = 0
ServQL()\LastError = #ServQL_DB_Not_Found
Else
If IsFile(DBFile)
CloseFile(DBFile)
EndIf
DBFile = OpenDatabase(#PB_Any, #ServQL_Path + DatabaseName$, User$, Password$)
If DBFile <> 0
ServQL()\LastError = #ServQL_DB_Opened
Else
CloseDatabase(DBFile)
ServQL()\LastError = #ServQL_DB_Creation_Error
ProcedureReturn #ServQL_DB_Creation_Error
EndIf
EndIf
ServQL()\DB = DBFile
ProcedureReturn DBFile
EndProcedure
Procedure.l ServQL_CloseDatabase(ClientID)
Define Database.l
SelectElement(ServQL(), ClientID)
Database = ServQL()\DB
If IsDatabase(Database)
CloseDatabase(Database)
EndIf
EndProcedure
Procedure.l ServQL_GetCount(ClientID, Table$)
Define Database.l, Anzahl.l
SelectElement(ServQL(), ClientID)
Database = ServQL()\DB
Anzahl = 0
If DatabaseQuery(Database, "SELECT COUNT(*) FROM " + Table$)
NextDatabaseRow(Database)
Anzahl = GetDatabaseLong(Database, 0)
FinishDatabaseQuery(Database)
EndIf
ProcedureReturn Anzahl
EndProcedure
Procedure.s ServQL_SetDatabaseBlob(ClientID)
SelectElement(ServQL(), ClientID)
SetDatabaseBlob(ServQL()\DB, ServQL()\Column, ServQL()\Blob, ServQL()\Size)
ProcedureReturn "SetDatabaseBlob"
EndProcedure
Procedure.s ServQL_GetDatabaseBlob(ClientID, Column)
Define Database.l, Temp$, *buffer
SelectElement(ServQL(), ClientID)
Database = ServQL()\DB
Temp$ = ServQL()\Request$ + " LIMIT " + Str(ServQL()\Cur) + ",1;"
DatabaseQuery(Database, Temp$)
NextDatabaseRow(Database)
ServQL()\Column = Column
ServQL()\Size = DatabaseColumnSize(Database, Column)
*buffer = AllocateMemory(ServQL()\Size)
ServQL()\Blob = AllocateMemory(ServQL()\Size)
; hier gibt es ein Problem?!?
GetDatabaseBlob(Database, Column, *buffer, ServQL()\Size)
CopyMemory(*buffer, ServQL()\Blob, ServQL()\Size)
If *buffer
FreeMemory(*buffer)
EndIf
; hier gibt es ein Problem?!?
ProcedureReturn "GetDatabaseBlob"
EndProcedure
Procedure.s ServQL_DatabaseQuery(ClientID, Request$)
Define Table$, Database.l, i.l, Anzahl.l, Additiv$
SelectElement(ServQL(), ClientID)
Table$ = ServQL_GetTable(Request$)
ServQL()\Request$ = Request$
ServQL()\Cur = -1
ServQL()\Max = ServQL_GetCount(ClientID, Table$)
; wenn es eine SELECT-Abfrage ist, dann hier bereits ausführen und ermitteln:
; - DatabaseColumns (Anzahl)
; - DatabaseColumnName (Namen der Spalten)
; - DatabaseColumnType (Typ der Spalten)
If FindString(UCase(Request$), "SELECT", 0) <> 0
Database = ServQL()\DB
DatabaseQuery(Database, Request$)
Anzahl = DatabaseColumns(Database)
Additiv$ = "°" + Str(Anzahl)
For i = 0 To Anzahl - 1
Additiv$ + "°" + DatabaseColumnName(Database, i) + "°" + Str(DatabaseColumnSize(Database, i)) + "°" + Str(DatabaseColumnType(Database, i))
Next i
FinishDatabaseQuery(Database)
EndIf
ProcedureReturn Str(ServQL()\Max) + Additiv$
EndProcedure
Procedure.l ServQL_NextDatabaseRow(ClientID)
Define Cur.l, Max.l
SelectElement(ServQL(), ClientID)
Cur = ServQL()\Cur
Max = ServQL()\Max
Cur + 1
If Cur > Max
Cur = Max
ServQL()\LastError = #ServQL_Last_Row
ProcedureReturn 0
EndIf
ServQL()\Cur = Cur
ProcedureReturn Max - Cur
EndProcedure
Procedure.l ServQL_PreviousDatabaseRow(ClientID)
Define Cur.l
SelectElement(ServQL(), ClientID)
Cur = ServQL()\Cur
If Cur = 0
Cur = 1
ServQL()\LastError = #ServQL_Last_Row
ProcedureReturn 0
EndIf
ServQL()\Cur = Cur
ProcedureReturn Cur
EndProcedure
Procedure.l ServQL_FirstDatabaseRow(ClientID)
SelectElement(ServQL(), ClientID)
ServQL()\Cur = - 1
ProcedureReturn ServQL()\Max - ServQL()\Cur
EndProcedure
Procedure.s ServQL_GetDatabaseString(ClientID)
Define Database.l, Anzahl.l, I.l, Temp$
SelectElement(ServQL(), ClientID)
Database = ServQL()\DB
Temp$ = ServQL()\Request$ + " LIMIT " + Str(ServQL()\Cur) + ",1;"
DatabaseQuery(Database, Temp$)
NextDatabaseRow(Database)
Anzahl = DatabaseColumns(Database)
Temp$ = ""
For I = 0 To Anzahl - 2
Temp$ + GetDatabaseString(Database, I) + "°"
Next i
Temp$ + GetDatabaseString(Database, Anzahl - 1)
FinishDatabaseQuery(Database)
ProcedureReturn Temp$
EndProcedure
Procedure.l ServQL_CheckDatabaseUpdate(ClientID, Request$)
SelectElement(ServQL(), ClientID)
DatabaseUpdate(ServQL()\DB, Request$)
; Debug DatabaseError()
EndProcedure
Procedure.l ServQL_FinishDatabaseQuery(ClientID)
SelectElement(ServQL(), ClientID)
FinishDatabaseQuery(ServQL()\DB)
EndProcedure
Procedure.s ServQL_CheckString(Request$, ClientID)
Define Order$, Order1$, Order2$, Order3$, Temp$
Order$ = StringField(Request$, 1, "°")
Order1$ = StringField(Request$, 2, "°")
Order2$ = StringField(Request$, 3, "°")
Order3$ = StringField(Request$, 4, "°")
Temp$ = ""
SelectElement(ServQL(), ClientID)
Select Order$
Case "CreateDatabase" : Temp$ = Str(ServQL_Createdatabase(Order1$, Order2$, Order3$))
Case "OpenDatabase" : Temp$ = Str(ServQL_OpenDatabase(Order1$, Order2$, Order3$, ClientID))
Case "DatabaseQuery" : Temp$ = ServQL_DatabaseQuery(ClientID, Order1$)
Case "FinishDatabaseQuery" : Temp$ = Str(ServQL_FinishDatabaseQuery(ClientID))
Case "NextDatabaseRow" : Temp$ = Str(ServQL_NextDatabaseRow(ClientID))
Case "PreviousDatabaseRow" : Temp$ = Str(ServQL_PreviousDatabaseRow(ClientID))
Case "FirstDatabaseRow" : Temp$ = Str(ServQL_FirstDatabaseRow(ClientID))
Case "GetDatabaseString" : Temp$ = ServQL_GetDatabaseString(ClientID)
Case "DatabaseUpdate" : Temp$ = Str(ServQL_CheckDatabaseUpdate(ClientID, Order1$))
Case "DatabaseError" : Temp$ = ServQL_DatabaseError(ClientID)
Case "CloseDatabase" : Temp$ = Str(ServQL_CloseDatabase(ClientID))
Case "DatabaseDriverName" : Temp$ = #Title + " v" + #Version
Case "DatabaseDriverDescription" : Temp$ = #Title + " v" + #Version + ": support for SQLite-orders via Network"
Case "SetDatabaseBlob" : If Val(Order2$) <> 0
SendNetworkString(ServQL()\ClientID, Order$ + "°Prepared. Send me " + Order2$)
ServQL()\Richtung = #ServQL_Blob_Upload
ServQL()\Column = Val(Order1$)
ServQL()\Size = Val(Order2$)
Else
SendNetworkString(ServQL()\ClientID, Order$ + "°0°I am out")
EndIf
Case "GetDatabaseBlob" : ServQL_GetDatabaseBlob(ClientID, Val(Order1$))
SendNetworkString(ServQL()\ClientID, Order$ + "°Prepared. I send you°" + Str(ServQL()\Size))
Case "GetDatabaseBlobRec" : SendNetworkData(ServQL()\ClientID, ServQL()\Blob, ServQL()\Size)
FreeMemory(ServQL()\Blob)
Case "Quit Server" : Temp$ = Order$
EndSelect
ServQL_Log(Order$ + " - " + Temp$)
If Temp$ <> ""
SendNetworkString(ServQL()\ClientID, Order$ + "°" + Temp$)
EndIf
ProcedureReturn Temp$
EndProcedure
Procedure.l ServQL_Init()
; Use SQLite-Database driver for file-based databases
UseSQLiteDatabase()
; Create or open Log-File
LogFile = OpenFile(#PB_Any, "/ServQLite.log")
If LogFile = 0
MessageRequester("Information", "Log-File could not be created!!")
End
EndIf
; Check, if Network available. If not, exit procedure
If InitNetwork() = 0
ServQL_Log("No network connection available.")
ProcedureReturn #ServQL_No_Network
EndIf
; create network-server. If not successul, exit procedure
If CreateNetworkServer(0,ServQL_Port) = 0
ProcedureReturn #ServQL_Port_Error
EndIf
ServQL_Log("ServQLite started on port " + Str(ServQL_Port))
EndProcedure
Procedure ServQL_CheckClients(*Value)
Define Zeit.l, Datenbank.l
Repeat
ForEach ServQL()
Zeit = ServQL()\LastConnect
Datenbank = ServQL()\DB
If Zeit + ClientTimeOut < ElapsedMilliseconds()
If IsDatabase(Datenbank)
CloseDatabase(Datenbank)
EndIf
Debug "server said: kicking client"
DeleteElement(ServQL())
EndIf
Next
Delay(*Value)
ForEver
EndProcedure
Procedure ServQL_AddClient(ClientID)
Define Doppelt.l
; Doppelt = 0
; ForEach ServQL()
; If ServQL()\ClientIP = GetClientIP(ClientID)
; SendNetworkString(ClientID, "Double-Connection For this IP. Sorry, no double... Please wait 10 minutes.")
; Debug "double connection from IP: " + IPString(GetClientIP(ClientID))
; Doppelt = 1
; EndIf
; Next
; If Doppelt = 0
AddElement(ServQL())
ServQL()\ClientID = ClientID
ServQL()\LastConnect = ElapsedMilliseconds()
ServQL()\ClientIP = GetClientIP(ClientID)
Debug "client connected: " + Str(ClientID) + " - IP: " + IPString(GetClientIP(ClientID))
; EndIf
EndProcedure
Procedure ServQL_CheckQuery(ClientID)
Define Temp$, *Buffer, Temp.l
SelectElement(ServQL(), ClientID)
Temp = ServQL()\Richtung
Select Temp
Case #ServQL_Blob_Upload
ServQL()\Blob = AllocateMemory(ServQL()\Size)
ReceiveNetworkData(ServQL()\ClientID, ServQL()\Blob, ServQL()\Size)
ServQL_SetDatabaseBlob(ClientID)
FreeMemory(ServQL()\Blob)
ServQL()\Richtung = 0
SendNetworkString(ServQL()\ClientID, "SetDatabaseBlob°" + Str(ServQL()\Size))
Default :
*Buffer = AllocateMemory(BufferSize)
ReceiveNetworkData(ServQL()\ClientID, *Buffer, BufferSize)
Temp$ = PeekS(*Buffer)
Debug "Client asked: " + Temp$
If ServQL()\ClientID = 0
SendNetworkString(ServQL()\ClientID, "Can't remember your ID. Please reconnect")
Else
ServQL()\LastConnect = ElapsedMilliseconds()
If ServQL_CheckString(Temp$, ClientID) = "Quit Server"
ProcedureReturn 1
EndIf
EndIf
FreeMemory(*Buffer)
EndSelect
EndProcedure
Procedure ServQL_LoadConfig()
OpenPreferences(location$ + "ServQLite.conf")
ServQL_Port = ReadPreferenceLong("ServQL_Port", ServQL_Port)
BufferSize = ReadPreferenceLong("BufferSize", BufferSize)
BlobSize = ReadPreferenceLong("BlobSize", BlobSize)
ClientTimeOut = ReadPreferenceLong("ClientTimeOut", ClientTimeOut)
CheckTimeOut = ReadPreferenceLong("CheckTimeOut", CheckTimeOut)
ClosePreferences()
EndProcedure
Procedure ServQL_SaveConfig()
CreatePreferences(location$ + "ServQLite.conf")
PreferenceGroup("ServQLite Settings")
PreferenceComment("ServQLPort is the port, on which ServQLite offers it's services")
WritePreferenceLong("ServQL_Port", ServQL_Port)
PreferenceComment("")
PreferenceComment("BufferSize defines, how much memory ServQLite uses for buffering")
WritePreferenceLong("BufferSize", BufferSize)
PreferenceComment("")
PreferenceComment("BlobSize defines the maximum size of a BLOB, e.g. an image")
WritePreferenceLong("BlobSize", BlobSize)
PreferenceComment("")
PreferenceComment("ClientTimeOut defines, how long a client will be held offline until it is being removed")
WritePreferenceLong("ClientTimeOut", ClientTimeOut)
PreferenceComment("")
PreferenceComment("CheckTimeOut defines, how often (in seconds) online-state of clients are checked")
WritePreferenceLong("CheckTimeOut", CheckTimeOut)
ClosePreferences()
Debug location$
EndProcedure
ServQL_LoadConfig()
If ServQL_Init() <> #ServQL_OK
ServQL_Log("Server could not be started. Quitting!")
Quit = 1
EndIf
CompilerIf #debug <> 0
OpenWindow(0,0,0,0,0,"ServQLite",#PB_Window_Invisible)
UsePNGImageDecoder()
CatchImage(0,?Icon)
AddSysTrayIcon(Num, WindowID(0), ImageID(ID))
SysTrayIconToolTip(Num, ToolTip$)
DataSection
Icon : IncludeBinary "icon.png"
EndDataSection
CompilerEndIf
CreateThread(@ServQL_CheckClients(), CheckTimeOut)
Repeat
SEvent = NetworkServerEvent()
CompilerIf #debug <> 0 ; Debug mode for Programming - will display the SysTray-Icon
Select WaitWindowEvent(50)
Case #PB_Event_SysTray
If EventType() = #PB_EventType_LeftClick
If MessageRequester("Question", "Really quit ServQLite?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
Quit = 1
EndIf
EndIf
EndSelect
CompilerEndIf
If SEvent
ClientID = EventClient()
Select SEvent
Case #PB_NetworkEvent_Connect : ServQL_AddClient(ClientID) ; registrieren eines clients
Case #PB_NetworkEvent_Data : Quit = ServQL_CheckQuery(FindeLinked(ClientID)) ; Anfrage eines Clients abfangen -> Threaded !?
Case #PB_NetworkEvent_Disconnect ; Client entfernen
ServQL_CloseDatabase(FindeLinked(ClientID))
SelectElement(ServQL(), ClientID)
DeleteElement(ServQL())
Debug "client is gone"
EndSelect
EndIf
Delay(0) ; give everything back to the System...
Until Quit = 1
CloseNetworkServer(0)
ServQL_Log("ServQLite quit.")