CHAT_MAX_USER_NAME = 20
CHAT_MAX_MESSAGE = 1000
CHAT_BACKLOG_DEFAULT = 100
proc ChatRealTime, .pSpecial
begin
pushad
mov esi, [.pSpecial]
stdcall ChatPermissions, esi
jc .error_no_permissions
stdcall InitEventSession, esi, evmMessage or evmUsersOnline or evmUserChanged, 0 ; if CF=0 returns session string in EAX
jc .exit
stdcall ChatInitialEvents, eax
stdcall StrDel, eax
.exit:
popad
xor eax, eax
stc ; all communications here are finished: CF=1 and EAX=0.
return
.error_no_permissions:
stdcall TextCreate, sizeof.TText
stdcall AppendError, eax, "403 Forbidden", esi
stdcall FCGI_outputText, [esi+TSpecialParams.hSocket], [esi+TSpecialParams.requestID], edx, TRUE
stdcall TextFree, edx
jmp .exit
endp
sqlSelectChat text "select id, time, user, original, message from ChatLog where id in (select id from chatlog order by id desc limit ?1)"
proc ChatInitialEvents, .session
.stmt dd ?
begin
pushad
xor ebx, ebx ; the count of the fetched messages.
lea eax, [.stmt]
cinvoke sqlitePrepare_v2, [hMainDatabase], sqlSelectChat, sqlSelectChat.length, eax, 0
mov eax, CHAT_BACKLOG_DEFAULT
stdcall GetParam, 'chat_backlog_length', gpInteger
cinvoke sqliteBindInt, [.stmt], 1, eax
stdcall StrDupMem, <txt '{ "msgs": [ '> ; start of the messages data set.
mov edi, eax
.fetch_loop_msg:
cinvoke sqliteStep, [.stmt]
cmp eax, SQLITE_ROW
jne .finish_msg_set
; is there previous record?
test ebx, ebx
jz .comma_ok1
stdcall StrCat, edi, txt ", "
.comma_ok1:
stdcall StrCat, edi, '{ "id": '
cinvoke sqliteColumnText, [.stmt], 0
stdcall StrCat, edi, eax
stdcall StrCat, edi, txt ', "time": '
cinvoke sqliteColumnText, [.stmt], 1
stdcall StrCat, edi, eax
stdcall StrCat, edi, txt ', "user": "'
cinvoke sqliteColumnText, [.stmt], 2
test eax, eax
jz @f
stdcall StrEncodeJS, eax
stdcall StrCat, edi, eax
stdcall StrDel, eax
@@:
stdcall StrCat, edi, txt '", "originalname": "'
cinvoke sqliteColumnText, [.stmt], 3
stdcall StrCat, edi, eax
stdcall StrCat, edi, txt '", "text": "'
cinvoke sqliteColumnText, [.stmt], 4
stdcall StrEncodeJS, eax
stdcall StrCat, edi, eax
stdcall StrDel, eax
stdcall StrCat, edi, txt '" }'
inc ebx
jmp .fetch_loop_msg
.finish_msg_set:
cinvoke sqliteFinalize, [.stmt]
test ebx, ebx
jz .messages_ok
stdcall StrCat, edi, txt " ] }"
stdcall AddEvent, evMessage, edi, [.session]
.messages_ok:
stdcall SendUsersOnline, [.session]
stdcall StrDel, edi
popad
return
endp
sqlPostChatMessage text "insert into chatlog (time, user, original, message) values (?1, ?2, ?3, ?4);"
sqlGetChatUser text "select username from EventSessions where session = ?1;"
proc ChatPage, .pSpecial
.stmt dd ?
begin
pushad
mov esi, [.pSpecial]
; the user permissions
stdcall ChatPermissions, esi
jc .error_no_permissions
cmp [esi+TSpecialParams.post_array], 0
jne .post_new_message
mov eax, [esi+TSpecialParams.userLang]
stdcall StrCat, [esi+TSpecialParams.page_title], [cChatTitle+8*eax]
stdcall LogUserActivity, esi, uaChatting, 0
stdcall RenderTemplate, 0, txt "chat.tpl", 0, [.pSpecial]
clc
mov [esp+4*regEAX], eax
popad
return
.error_no_permissions:
stdcall TextCreate, sizeof.TText
stdcall AppendError, eax, "403 Forbidden", esi
jmp .finish_replace
.post_new_message:
xor edi, edi
stdcall GetCookieValue, [esi+TSpecialParams.params], "eventsid"
jc .error_no_permissions
mov edi, eax
stdcall GetPostString, [esi+TSpecialParams.post_array], txt "cmd", 0
test eax, eax
jz .finish
mov ecx, .post_message
stdcall StrCompCase, eax, txt "message"
jc .do_it
mov ecx, .rename_user
stdcall StrCompCase, eax, txt "rename"
jc .do_it
mov ecx, .change_status
stdcall StrCompCase, eax, txt "status"
jc .do_it
mov ecx, .finish
.do_it:
stdcall StrDel, eax
jmp ecx
.post_message:
locals
.id dd ?
.time dd ?
.user dd ?
.original dd ?
.text dd ?
endl
stdcall GetTime
xor ecx, ecx
mov [.id], ecx
mov [.time], eax
mov [.user], ecx
mov [.original], ecx
mov [.text], ecx
stdcall GetPostString, [esi+TSpecialParams.post_array], txt "chat_message", 0
mov [.text], eax
test eax, eax
jz .finish
; message text sanitation
stdcall StrByteUtf8, [.text], CHAT_MAX_MESSAGE
stdcall StrTrim, [.text], eax
stdcall StrClipSpacesR, [.text]
stdcall StrClipSpacesL, [.text]
stdcall StrLen, [.text]
test eax, eax
jz .finish
lea eax, [.stmt]
cinvoke sqlitePrepare_v2, [hMainDatabase], sqlGetChatUser, sqlGetChatUser.length, eax, 0
stdcall StrPtr, edi
cinvoke sqliteBindText, [.stmt], 1, eax, [eax+string.len], SQLITE_STATIC
cinvoke sqliteStep, [.stmt]
cmp eax, SQLITE_ROW
jne .username_ok
cinvoke sqliteColumnText, [.stmt], 0
stdcall StrDupMem, eax
mov [.user], eax
.username_ok:
cinvoke sqliteFinalize, [.stmt]
stdcall EventUserName, [.pSpecial]
mov [.original], eax
cinvoke sqliteExec, [hMainDatabase], sqlBegin, 0, 0, 0
lea eax, [.stmt]
cinvoke sqlitePrepare_v2, [hMainDatabase], sqlPostChatMessage, sqlPostChatMessage.length, eax, 0
cinvoke sqliteBindInt, [.stmt], 1, [.time]
cmp [.user], 0
je .username_ok2
stdcall StrPtr, [.user]
cinvoke sqliteBindText, [.stmt], 2, eax, [eax+string.len], SQLITE_STATIC
.username_ok2:
stdcall StrPtr, [.original]
cinvoke sqliteBindText, [.stmt], 3, eax, [eax+string.len], SQLITE_STATIC
stdcall StrPtr, [.text]
cinvoke sqliteBindText, [.stmt], 4, eax, [eax+string.len], SQLITE_STATIC
cinvoke sqliteStep, [.stmt]
mov ebx, sqlRollback
cmp eax, SQLITE_DONE
jne .finish_query
mov ebx, sqlCommit
cinvoke sqliteLastInsertRowID, [hMainDatabase]
mov [.id], eax
.finish_query:
cinvoke sqliteFinalize, [.stmt]
cinvoke sqliteExec, [hMainDatabase], ebx, 0, 0, 0
cmp [.id], 0 ; the insert in the chatlog has been failed and the ID is unknown.
je .finish ; ... so, don't create an event.
stdcall FormatJsonMessage, [.id], [.time], [.user], [.original], [.text]
stdcall AddEvent, evMessage, eax, 0
stdcall StrDel, eax
.finish:
stdcall StrDel, [.user]
stdcall StrDel, [.original]
stdcall StrDel, [.text]
stdcall StrDel, edi ; the chatsid string.
stdcall TextCreate, sizeof.TText
stdcall TextCat, eax, <"Content-type: text/plain", 13, 10, 13, 10, "OK", 13, 10>
.finish_replace:
stc
mov [esp+4*regEAX], edx
popad
return
.rename_user:
stdcall GetPostString, [esi+TSpecialParams.post_array], "username", txt " "
mov edx, eax
stdcall EventUserName, esi
stdcall RenameEventUser, edi, edx, eax
stdcall StrDel, eax
stdcall StrDel, edx
jmp .finish
.change_status:
stdcall GetPostString, [esi+TSpecialParams.post_array], "status", 0
test eax, eax
jz .finish
push eax
stdcall StrToNumEx, eax
stdcall StrDel ; from the stack
stdcall SetEventStatus, edi, eax
jmp .finish
endp
proc ChatPermissions, .pSpecial
begin
push eax esi
mov esi, [.pSpecial]
stdcall GetParam, "chat_enabled", gpInteger
jc .not_ok
test eax, eax
jz .not_ok
test [esi+TSpecialParams.userStatus], permChat or permAdmin
jnz .permissions_ok
.not_ok:
stc
pop esi eax
return
.permissions_ok:
clc
pop esi eax
return
endp
proc FormatJsonMessage, .id, .time, .user, .original, .text
begin
pushad
stdcall StrDupMem, txt '{ "msgs": [ '
mov edi, eax
stdcall StrCat, edi, '{ "id": '
stdcall NumToStr, [.id], ntsDec or ntsUnsigned
stdcall StrCat, edi, eax
stdcall StrDel, eax
stdcall StrCat, edi, txt ', "time": '
stdcall NumToStr, [.time], ntsDec or ntsUnsigned
stdcall StrCat, edi, eax
stdcall StrDel, eax
stdcall StrCat, edi, txt ', "user": "'
cmp [.user], 0
je @f
stdcall StrEncodeJS, [.user]
stdcall StrCat, edi, eax
stdcall StrDel, eax
@@:
stdcall StrCat, edi, txt '", "originalname": "'
stdcall StrCat, edi, [.original]
stdcall StrCat, edi, txt '", "text": "'
stdcall StrEncodeJS, [.text]
stdcall StrCat, edi, eax
stdcall StrDel, eax
stdcall StrCat, edi, txt '" } ] }'
mov [esp+4*regEAX], edi
popad
return
endp
cCRLF2 text 13, 10, 13, 10
if defined options.DebugWebSSE & options.DebugWebSSE
cContentTypeEvent2 text 'Content-Type: text/event-stream', 13, 10, "X-Accel-Buffering: yes", 13, 10, 13, 10, "retry: 1000", 13, 10, 13, 10
proc EchoRealTime, .pSpecialParams
.bytes dd ?
begin
pushad
DebugMsg "Started echo long life thread!"
mov esi, [.pSpecialParams]
cmp [fEventsTerminate], 0
jne .finish_socket
mov edi, cContentTypeEvent
mov ecx, cContentTypeEvent.length
cmp [esi+TSpecialParams.page_num], 0
je @f
mov edi, cContentTypeEvent2
mov ecx, cContentTypeEvent2.length
@@:
stdcall FCGI_output, [esi+TSpecialParams.hSocket], [esi+TSpecialParams.requestID], edi, ecx, FALSE
stdcall StrDupMem, <txt 'event: message', 13, 10, 'data: '> ; echo message
mov edi, eax
mov ebx, cContentTypeEvent.length
.event_loop_msg:
stdcall StrPtr, edi
stdcall FCGI_output, [esi+TSpecialParams.hSocket], [esi+TSpecialParams.requestID], eax, [eax+string.len], FALSE
jc .finish
stdcall NumToStr, ebx, ntsDec or ntsUnsigned or ntsFixedWidth + 6
push eax
stdcall StrPtr, eax
stdcall FCGI_output, [esi+TSpecialParams.hSocket], [esi+TSpecialParams.requestID], eax, [eax+string.len], FALSE
stdcall StrDel ; from the stack
jc .finish
stdcall FCGI_output, [esi+TSpecialParams.hSocket], [esi+TSpecialParams.requestID], cCRLF2, cCRLF2.length, FALSE
jc .finish
cmp [fEventsTerminate], 0
jne .finish_socket
add ebx, 32 ; one event lenght
stdcall Sleep, 100
jmp .event_loop_msg
.finish_socket:
stdcall FCGI_output, [esi+TSpecialParams.hSocket], [esi+TSpecialParams.requestID], 0, 0, TRUE
.finish:
stdcall StrDel, edi
DebugMsg "Finished echo long life thread!"
popad
xor eax, eax
stc
return
endp
else
EchoRealTime = 0
end if