2 * This module handles instant messaging between users.
4 * Copyright (c) 2012-2022 by the citadel.org team
6 * This program is open source software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 #include "../../sysdep.h"
22 #include <sys/types.h>
28 #include <libcitadel.h>
29 #include "../../citadel.h"
30 #include "../../server.h"
31 #include "../../citserver.h"
32 #include "../../support.h"
33 #include "../../config.h"
34 #include "../../msgbase.h"
35 #include "../../user_ops.h"
36 #include "../../ctdl_module.h"
47 struct chatmsg *first_chat_msg = NULL;
48 struct chatmsg *last_chat_msg = NULL;
52 * Periodically called for housekeeping. Expire old chat messages so they don't take up memory forever.
54 void roomchat_timer(void) {
57 begin_critical_section(S_CHATQUEUE);
59 while ((first_chat_msg != NULL) && ((time(NULL) - first_chat_msg->timestamp) > 300)) {
60 ptr = first_chat_msg->next;
61 free(first_chat_msg->sender);
62 free(first_chat_msg->msgtext);
65 if (first_chat_msg == NULL) {
70 end_critical_section(S_CHATQUEUE);
75 * Perform shutdown-related activities...
77 void roomchat_shutdown(void) {
78 /* if we ever start logging chats, we have to flush them to disk here .*/
83 * Add a message into the chat queue
85 void add_to_chat_queue(char *msg) {
88 struct chatmsg *m = malloc(sizeof(struct chatmsg));
92 m->timestamp = time(NULL);
93 m->roomnum = CC->room.QRnumber;
94 m->sender = strdup(CC->user.fullname);
95 m->msgtext = strdup(msg);
97 if ((m->sender == NULL) || (m->msgtext == NULL)) {
104 begin_critical_section(S_CHATQUEUE);
107 if (first_chat_msg == NULL) {
108 assert(last_chat_msg == NULL);
113 assert(last_chat_msg != NULL);
114 assert(last_chat_msg->next == NULL);
115 last_chat_msg->next = m;
119 end_critical_section(S_CHATQUEUE);
124 * Transmit a message into a room chat
126 void roomchat_send(char *argbuf) {
129 if ((CC->cs_flags & CS_CHAT) == 0) {
130 cprintf("%d Session is not in chat mode.\n", ERROR);
134 cprintf("%d send now\n", SEND_LISTING);
135 while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) {
136 add_to_chat_queue(buf);
142 * Poll room for incoming chat messages
144 void roomchat_poll(char *argbuf) {
146 struct chatmsg *found = NULL;
147 struct chatmsg *ptr = NULL;
149 newer_than = extract_int(argbuf, 1);
151 if ((CC->cs_flags & CS_CHAT) == 0) {
152 cprintf("%d Session is not in chat mode.\n", ERROR);
156 begin_critical_section(S_CHATQUEUE);
157 for (ptr = first_chat_msg; ((ptr != NULL) && (found == NULL)); ptr = ptr->next) {
158 if ((ptr->seq > newer_than) && (ptr->roomnum == CC->room.QRnumber)) {
162 end_critical_section(S_CHATQUEUE);
165 cprintf("%d no messages\n", ERROR + MESSAGE_NOT_FOUND);
169 cprintf("%d %d|%ld|%s\n", LISTING_FOLLOWS, found->seq, found->timestamp, found->sender);
170 cprintf("%s\n", found->msgtext);
177 * list users in chat in this room
179 void roomchat_rwho(char *argbuf) {
180 struct CitContext *nptr;
183 if ((CC->cs_flags & CS_CHAT) == 0) {
184 cprintf("%d Session is not in chat mode.\n", ERROR);
188 cprintf("%d%c \n", LISTING_FOLLOWS, CtdlCheckExpress() );
190 nptr = CtdlGetContextArray(&nContexts) ; // grab a copy of the wholist
192 for (i=0; i<nContexts; i++) { // list the users
193 if ( (nptr[i].room.QRnumber == CC->room.QRnumber)
194 && (nptr[i].cs_flags & CS_CHAT)
196 cprintf("%s\n", nptr[i].user.fullname);
199 free(nptr); // free our copy
208 * Participate in real time chat in a room
210 void cmd_rcht(char *argbuf)
214 if (CtdlAccessCheck(ac_logged_in)) return;
216 extract_token(subcmd, argbuf, 0, '|', sizeof subcmd);
218 if (!strcasecmp(subcmd, "enter")) {
219 CC->cs_flags |= CS_CHAT;
220 cprintf("%d Entering chat mode.\n", CIT_OK);
222 else if (!strcasecmp(subcmd, "exit")) {
223 CC->cs_flags &= ~CS_CHAT;
224 cprintf("%d Exiting chat mode.\n", CIT_OK);
226 else if (!strcasecmp(subcmd, "send")) {
227 roomchat_send(argbuf);
229 else if (!strcasecmp(subcmd, "poll")) {
230 roomchat_poll(argbuf);
232 else if (!strcasecmp(subcmd, "rwho")) {
233 roomchat_rwho(argbuf);
236 cprintf("%d Invalid subcommand\n", ERROR + CMD_NOT_SUPPORTED);
241 // Initialization function, called from modules_init.c
242 char *ctdl_module_init_roomchat(void) {
244 CtdlRegisterProtoHook(cmd_rcht, "RCHT", "Participate in real time chat in a room");
245 CtdlRegisterSessionHook(roomchat_timer, EVT_TIMER, PRIO_CLEANUP + 400);
246 CtdlRegisterSessionHook(roomchat_shutdown, EVT_SHUTDOWN, PRIO_SHUTDOWN + 55);
249 /* return our module name for the log */