/*
** Copyright (c) 2020 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to implement the Fossil chatroom.
**
** Initial design goals:
**
** * Keep it simple. This chatroom is not intended as a competitor
** or replacement for IRC, Discord, Telegram, Slack, etc. The goal
** is zero- or near-zero-configuration, not an abundance of features.
**
** * Intended as a place for insiders to have ephemeral conversations
** about a project. This is not a public gather place. Think
** "boardroom", not "corner pub".
**
** * One chatroom per repository.
**
** * Chat content lives in a single repository. It is never synced.
** Content expires and is deleted after a set interval (a week or so).
**
** Notification is accomplished using the "hanging GET" or "long poll" design
** in which a GET request is issued but the server does not send a reply until
** new content arrives. Newer Web Sockets and Server Sent Event protocols are
** more elegant, but are not compatible with CGI, and would thus complicate
** configuration.
*/
#include "config.h"
#include <assert.h>
#include "chat.h"
/*
** WEBPAGE: chat
**
** Start up a browser-based chat session.
*/
void chat_webpage(void){
login_check_credentials();
style_set_current_feature("chat");
if( !g.perm.Chat ){
style_header("Chat Not Authorized");
@ <h1>Not Authorized</h1>
@ <p>You do not have permission to use the chatroom on this
@ repository.</p>
style_finish_page();
return;
}
style_header("Chat");
@ <style>
@ #dialog {
@ width: 97%%;
@ }
@ #chat-input-area {
@ width: 100%%;
@ display: flex;
@ flex-direction: column;
@ }
@ #chat-input-line {
@ display: flex;
@ flex-direction: row;
@ margin-bottom: 1em;
@ align-items: center;
@ }
@ #chat-input-line > input[type=submit] {
@ flex: 1 5 auto;
@ max-width: 6em;
@ }
@ #chat-input-line > input[type=text] {
@ flex: 5 1 auto;
@ }
@ #chat-input-file {
@ display: flex;
@ flex-direction: row;
@ align-items: center;
@ }
@ #chat-input-file > input {
@ flex: 1 0 auto;
@ }
@ </style>
@ <form accept-encoding="utf-8" id="chat-form">
@ <div id='chat-input-area'>
@ <div id='chat-input-line'>
@ <input type="text" name="msg" id="sbox"\
@ placeholder="Type message here.">
@ <input type="submit" value="Send">
@ </div>
@ <div id='chat-input-file'>
@ <span>File:</span>
@ <input type="file" name="file">
@ </div>
@ </div>
@ </form>
@ <hr>
/* New chat messages get inserted immediately after this element */
@ <span id='message-inject-point'></span>
style_finish_page();
}
/* Definition of repository tables used by chat
*/
static const char zChatSchema1[] =
@ CREATE TABLE chat(
@ msgid INTEGER PRIMARY KEY AUTOINCREMENT,
@ mtime JULIANDAY,
@ xfrom TEXT,
@ xmsg TEXT,
@ file BLOB,
@ fname TEXT,
@ fmime TEXT
@ );
;
/*
** Make sure the repository data tables used by chat exist. Create them
** if they do not.
*/
static void chat_create_tables(void){
if( !db_table_exists("repository","chat") ){
db_multi_exec(zChatSchema1/*works-like:""*/);
}
}
/*
** WEBPAGE: chat-send
**
** This page receives (via XHR) a new chat-message and/or a new file
** to be entered into the chat history.
*/
void chat_send_webpage(void){
login_check_credentials();
if( !g.perm.Chat ) return;
chat_create_tables();
}
/*
** WEBPAGE: chat-poll
**
** The chat page generated by /chat using a XHR to this page in order
** to ask for new chat content. The "name" argument should begin with
** an integer which is the largest "msgid" that the chat page currently
** holds. If newer content is available, this routine returns that
** content straight away. If no new content is available, this webpage
** blocks until the new content becomes available. In this way, the
** system implements "hanging-GET" or "long-poll" style event notification.
**
** The reply from this webpage is JSON that describes the new content.
** Format of the json:
**
** {
** "msg": [
** {
** "msgid": integer // message id
** "mtime": text // When sent: YYYY-MM-DD HH:MM:SS UTC
** "xfrom": text // Login name of sender
** "uclr": text // Color string associated with the user
** "xmsg": text // HTML text of the message
** "fsize": integer // file attachment size in bytes
** "fname": text // Name of file attachment
** "fmime": text // MIME-type of file attachment
** }
** ]
** }
**
** The "fname" and "fmime" fields are only present if "fsize" is greater
** than zero. The "xmsg" field may be an empty string if "fsize" is zero.
**
** The "msgid" values will be in increasing order.
*/
void chat_poll_webpage(void){
login_check_credentials();
if( !g.perm.Chat ) return;
chat_create_tables();
cgi_set_content_type("text/json");
}
/*
** WEBPAGE: chat-download
**
** Download the CHAT.FILE attachment associated with a single chat
** entry. The "name" query parameter begins with an integer that
** identifies the particular chat message.
*/
void chat_download_webpage(void){
login_check_credentials();
if( !g.perm.Chat ) return;
chat_create_tables();
}