Fossil

chat.c at [217b0d2548]
Login

chat.c at [217b0d2548]

File src/chat.c artifact f673a7192f part of check-in 217b0d2548


/*
** 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();
}