4 * This module handles all "real time" communication between users. The
5 * modes of communication currently supported are Chat and Paging.
16 #include <sys/types.h>
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
23 # include <sys/time.h>
32 #include <libcitadel.h>
35 #include "serv_chat.h"
36 #include "citserver.h"
48 #include "ctdl_module.h"
52 struct ChatLine
*ChatQueue
= NULL
;
56 * This message can be set to anything you want, but it is
57 * checked for consistency so don't move it away from here.
59 #define KICKEDMSG "You have been kicked out of this room."
61 void allwrite(char *cmdbuf
, int flag
, char *username
)
66 struct ChatLine
*clptr
, *clnew
;
69 if (CC
->fake_username
[0])
70 un
= CC
->fake_username
;
72 un
= CC
->user
.fullname
;
74 snprintf(bcast
, sizeof bcast
, ":|<%s %s>", un
, cmdbuf
);
75 } else if (flag
== 0) {
76 snprintf(bcast
, sizeof bcast
, "%s|%s", un
, cmdbuf
);
77 } else if (flag
== 2) {
78 snprintf(bcast
, sizeof bcast
, ":|<%s whispers %s>", un
, cmdbuf
);
79 } else if (flag
== 3) {
80 snprintf(bcast
, sizeof bcast
, ":|%s", KICKEDMSG
);
82 if ((strcasecmp(cmdbuf
, "NOOP")) && (flag
!= 2)) {
83 fp
= fopen(CHATLOG
, "a");
85 fprintf(fp
, "%s\n", bcast
);
88 clnew
= (struct ChatLine
*) malloc(sizeof(struct ChatLine
));
89 memset(clnew
, 0, sizeof(struct ChatLine
));
91 fprintf(stderr
, "citserver: cannot alloc chat line: %s\n",
97 clnew
->chat_time
= now
;
98 safestrncpy(clnew
->chat_room
, CC
->room
.QRname
,
99 sizeof clnew
->chat_room
);
100 clnew
->chat_room
[sizeof clnew
->chat_room
- 1] = 0;
102 safestrncpy(clnew
->chat_username
, username
,
103 sizeof clnew
->chat_username
);
104 clnew
->chat_username
[sizeof clnew
->chat_username
- 1] = 0;
106 clnew
->chat_username
[0] = '\0';
107 safestrncpy(clnew
->chat_text
, bcast
, sizeof clnew
->chat_text
);
109 /* Here's the critical section.
110 * First, add the new message to the queue...
112 begin_critical_section(S_CHATQUEUE
);
114 clnew
->chat_seq
= ChatLastMsg
;
115 if (ChatQueue
== NULL
) {
118 for (clptr
= ChatQueue
; clptr
->next
!= NULL
; clptr
= clptr
->next
);;
122 /* Then, before releasing the lock, free the expired messages */
123 while ((ChatQueue
!= NULL
) && (now
- ChatQueue
->chat_time
>= 120L)) {
125 ChatQueue
= ChatQueue
->next
;
128 end_critical_section(S_CHATQUEUE
);
132 t_context
*find_context(char **unstr
)
134 t_context
*t_cc
, *found_cc
= NULL
;
137 if ((!*unstr
) || (!unstr
))
140 begin_critical_section(S_SESSION_TABLE
);
141 for (t_cc
= ContextList
; ((t_cc
) && (!found_cc
)); t_cc
= t_cc
->next
) {
142 if (t_cc
->fake_username
[0])
143 name
= t_cc
->fake_username
;
145 name
= t_cc
->curr_user
;
147 if ((!strncasecmp(name
, tptr
, strlen(name
))) && (tptr
[strlen(name
)] == ' ')) {
149 *unstr
= &(tptr
[strlen(name
) + 1]);
152 end_critical_section(S_SESSION_TABLE
);
158 * List users in chat.
159 * allflag == 0 = list users in chat
160 * 1 = list users in chat, followed by users not in chat
161 * 2 = display count only
164 void do_chat_listing(int allflag
)
166 struct CitContext
*ccptr
;
168 int count_elsewhere
= 0;
169 char roomname
[ROOMNAMELEN
];
171 if ((allflag
== 0) || (allflag
== 1))
172 cprintf(":|\n:| Users currently in chat:\n");
173 begin_critical_section(S_SESSION_TABLE
);
174 for (ccptr
= ContextList
; ccptr
!= NULL
; ccptr
= ccptr
->next
) {
175 if (ccptr
->cs_flags
& CS_CHAT
) {
176 if (!strcasecmp(ccptr
->room
.QRname
,
185 GenerateRoomDisplay(roomname
, ccptr
, CC
);
186 if ((CC
->user
.axlevel
< 6)
187 && (!IsEmptyStr(ccptr
->fake_roomname
))) {
188 strcpy(roomname
, ccptr
->fake_roomname
);
191 if ((ccptr
->cs_flags
& CS_CHAT
)
192 && ((ccptr
->cs_flags
& CS_STEALTH
) == 0)) {
193 if ((allflag
== 0) || (allflag
== 1)) {
194 cprintf(":| %-25s <%s>:\n",
195 (ccptr
->fake_username
[0]) ? ccptr
->fake_username
: ccptr
->curr_user
,
202 cprintf(":|\n:| Users not in chat:\n");
203 for (ccptr
= ContextList
; ccptr
!= NULL
; ccptr
= ccptr
->next
) {
205 GenerateRoomDisplay(roomname
, ccptr
, CC
);
206 if ((CC
->user
.axlevel
< 6)
207 && (!IsEmptyStr(ccptr
->fake_roomname
))) {
208 strcpy(roomname
, ccptr
->fake_roomname
);
211 if (((ccptr
->cs_flags
& CS_CHAT
) == 0)
212 && ((ccptr
->cs_flags
& CS_STEALTH
) == 0)) {
213 cprintf(":| %-25s <%s>:\n",
214 (ccptr
->fake_username
[0]) ? ccptr
->fake_username
: ccptr
->curr_user
,
219 end_critical_section(S_SESSION_TABLE
);
223 cprintf(":|There are %d users here.\n", count
);
226 cprintf(":|Note: you are the only one here.\n");
228 if (count_elsewhere
> 0) {
229 cprintf(":|There are %d users chatting in other rooms.\n", count_elsewhere
);
237 void cmd_chat(char *argbuf
)
242 int MyLastMsg
, ThisLastMsg
;
243 struct ChatLine
*clptr
;
244 struct CitContext
*t_context
;
247 if (!(CC
->logged_in
)) {
248 cprintf("%d Not logged in.\n", ERROR
+ NOT_LOGGED_IN
);
252 CC
->cs_flags
= CC
->cs_flags
| CS_CHAT
;
253 cprintf("%d Entering chat mode (type '/help' for available commands)\n",
257 MyLastMsg
= ChatLastMsg
;
259 if ((CC
->cs_flags
& CS_STEALTH
) == 0) {
260 allwrite("<entering chat>", 0, NULL
);
271 linelen
= strlen(cmdbuf
);
272 if (linelen
> 100) --linelen
; /* truncate too-long lines */
273 cmdbuf
[linelen
+ 1] = 0;
275 retval
= client_read_to(&cmdbuf
[linelen
], 1, 2);
277 if (retval
< 0 || CC
->kill_me
) { /* socket broken? */
278 if ((CC
->cs_flags
& CS_STEALTH
) == 0) {
279 allwrite("<disconnected>", 0, NULL
);
284 /* if we have a complete line, do send processing */
285 if (!IsEmptyStr(cmdbuf
))
286 if (cmdbuf
[strlen(cmdbuf
) - 1] == 10) {
287 cmdbuf
[strlen(cmdbuf
) - 1] = 0;
291 if ((!strcasecmp(cmdbuf
, "exit"))
292 || (!strcasecmp(cmdbuf
, "/exit"))
293 || (!strcasecmp(cmdbuf
, "quit"))
294 || (!strcasecmp(cmdbuf
, "logout"))
295 || (!strcasecmp(cmdbuf
, "logoff"))
296 || (!strcasecmp(cmdbuf
, "/q"))
297 || (!strcasecmp(cmdbuf
, ".q"))
298 || (!strcasecmp(cmdbuf
, "/quit"))
300 strcpy(cmdbuf
, "000");
302 if (!strcmp(cmdbuf
, "000")) {
303 if ((CC
->cs_flags
& CS_STEALTH
) == 0) {
304 allwrite("<exiting chat>", 0, NULL
);
308 CC
->cs_flags
= CC
->cs_flags
- CS_CHAT
;
311 if ((!strcasecmp(cmdbuf
, "/help"))
312 || (!strcasecmp(cmdbuf
, "help"))
313 || (!strcasecmp(cmdbuf
, "/?"))
314 || (!strcasecmp(cmdbuf
, "?"))) {
316 cprintf(":|Available commands: \n");
317 cprintf(":|/help (prints this message) \n");
318 cprintf(":|/who (list users currently in chat) \n");
319 cprintf(":|/whobbs (list users in chat -and- elsewhere) \n");
320 cprintf(":|/me ('action' line, ala irc) \n");
321 cprintf(":|/msg (send private message, ala irc) \n");
322 if (is_room_aide()) {
323 cprintf(":|/kick (kick another user out of this room) \n");
325 cprintf(":|/quit (exit from this chat) \n");
329 if (!strcasecmp(cmdbuf
, "/who")) {
333 if (!strcasecmp(cmdbuf
, "/whobbs")) {
337 if (!strncasecmp(cmdbuf
, "/me ", 4)) {
338 allwrite(&cmdbuf
[4], 1, NULL
);
341 if (!strncasecmp(cmdbuf
, "/msg ", 5)) {
343 strptr1
= &cmdbuf
[5];
344 if ((t_context
= find_context(&strptr1
))) {
345 allwrite(strptr1
, 2, CC
->curr_user
);
346 if (strcasecmp(CC
->curr_user
, t_context
->curr_user
))
347 allwrite(strptr1
, 2, t_context
->curr_user
);
349 cprintf(":|User not found.\n");
352 /* The /kick function is implemented by sending a specific
353 * message to the kicked-out user's context. When that message
354 * is processed by the read loop, that context will exit.
356 if ( (!strncasecmp(cmdbuf
, "/kick ", 6)) && (is_room_aide()) ) {
358 strptr1
= &cmdbuf
[6];
359 strcat(strptr1
, " ");
360 if ((t_context
= find_context(&strptr1
))) {
361 if (strcasecmp(CC
->curr_user
, t_context
->curr_user
))
362 allwrite(strptr1
, 3, t_context
->curr_user
);
364 cprintf(":|User not found.\n");
367 if ((cmdbuf
[0] != '/') && (strlen(cmdbuf
) > 0)) {
369 allwrite(cmdbuf
, 0, NULL
);
371 if ((!ok_cmd
) && (cmdbuf
[0]) && (cmdbuf
[0] != '\n'))
372 cprintf(":|Command %s is not understood.\n", cmdbuf
);
377 /* now check the queue for new incoming stuff */
379 if (CC
->fake_username
[0])
380 un
= CC
->fake_username
;
383 if (ChatLastMsg
> MyLastMsg
) {
384 ThisLastMsg
= ChatLastMsg
;
385 for (clptr
= ChatQueue
; clptr
!= NULL
; clptr
= clptr
->next
) {
386 if ((clptr
->chat_seq
> MyLastMsg
) && ((!clptr
->chat_username
[0]) || (!strncasecmp(un
, clptr
->chat_username
, 32)))) {
387 if ((!clptr
->chat_room
[0]) || (!strncasecmp(CC
->room
.QRname
, clptr
->chat_room
, ROOMNAMELEN
))) {
388 /* Output new chat data */
389 cprintf("%s\n", clptr
->chat_text
);
391 /* See if we've been force-quitted (kicked etc.) */
392 if (!strcmp(&clptr
->chat_text
[2], KICKEDMSG
)) {
393 allwrite("<kicked out of this room>", 0, NULL
);
395 CC
->cs_flags
= CC
->cs_flags
- CS_CHAT
;
397 /* Kick user out of room */
398 CtdlInvtKick(CC
->user
.fullname
, 0);
400 /* And return to the Lobby */
401 usergoto(config
.c_baseroom
, 0, 0, NULL
, NULL
);
407 MyLastMsg
= ThisLastMsg
;
415 * Delete any remaining instant messages
417 void delete_instant_messages(void) {
418 struct ExpressMessage
*ptr
;
420 begin_critical_section(S_SESSION_TABLE
);
421 while (CC
->FirstExpressMessage
!= NULL
) {
422 ptr
= CC
->FirstExpressMessage
->next
;
423 if (CC
->FirstExpressMessage
->text
!= NULL
)
424 free(CC
->FirstExpressMessage
->text
);
425 free(CC
->FirstExpressMessage
);
426 CC
->FirstExpressMessage
= ptr
;
428 end_critical_section(S_SESSION_TABLE
);
435 * Poll for instant messages (OLD METHOD -- ***DEPRECATED ***)
437 void cmd_pexp(char *argbuf
)
439 struct ExpressMessage
*ptr
, *holdptr
;
441 if (CC
->FirstExpressMessage
== NULL
) {
442 cprintf("%d No instant messages waiting.\n", ERROR
+ MESSAGE_NOT_FOUND
);
445 begin_critical_section(S_SESSION_TABLE
);
446 ptr
= CC
->FirstExpressMessage
;
447 CC
->FirstExpressMessage
= NULL
;
448 end_critical_section(S_SESSION_TABLE
);
450 cprintf("%d Express msgs:\n", LISTING_FOLLOWS
);
451 while (ptr
!= NULL
) {
452 if (ptr
->flags
&& EM_BROADCAST
)
453 cprintf("Broadcast message ");
454 else if (ptr
->flags
&& EM_CHAT
)
455 cprintf("Chat request ");
456 else if (ptr
->flags
&& EM_GO_AWAY
)
457 cprintf("Please logoff now, as requested ");
460 cprintf("from %s:\n", ptr
->sender
);
461 if (ptr
->text
!= NULL
)
462 memfmout(ptr
->text
, 0, "\n");
465 if (ptr
->text
!= NULL
) free(ptr
->text
);
474 * Get instant messages (new method)
476 void cmd_gexp(char *argbuf
) {
477 struct ExpressMessage
*ptr
;
479 if (CC
->FirstExpressMessage
== NULL
) {
480 cprintf("%d No instant messages waiting.\n", ERROR
+ MESSAGE_NOT_FOUND
);
484 begin_critical_section(S_SESSION_TABLE
);
485 ptr
= CC
->FirstExpressMessage
;
486 CC
->FirstExpressMessage
= CC
->FirstExpressMessage
->next
;
487 end_critical_section(S_SESSION_TABLE
);
489 cprintf("%d %d|%ld|%d|%s|%s|%s\n",
491 ((ptr
->next
!= NULL
) ? 1 : 0), /* more msgs? */
492 (long)ptr
->timestamp
, /* time sent */
493 ptr
->flags
, /* flags */
494 ptr
->sender
, /* sender of msg */
495 config
.c_nodename
, /* static for now (and possibly deprecated) */
496 ptr
->sender_email
/* email or jid of sender */
499 if (ptr
->text
!= NULL
) {
500 memfmout(ptr
->text
, 0, "\n");
501 if (ptr
->text
[strlen(ptr
->text
)-1] != '\n') cprintf("\n");
510 * Asynchronously deliver instant messages
512 void cmd_gexp_async(void) {
514 /* Only do this if the session can handle asynchronous protocol */
515 if (CC
->is_async
== 0) return;
517 /* And don't do it if there's nothing to send. */
518 if (CC
->FirstExpressMessage
== NULL
) return;
520 cprintf("%d instant msg\n", ASYNC_MSG
+ ASYNC_GEXP
);
524 * Back end support function for send_instant_message() and company
526 void add_xmsg_to_context(struct CitContext
*ccptr
,
527 struct ExpressMessage
*newmsg
)
529 struct ExpressMessage
*findend
;
531 if (ccptr
->FirstExpressMessage
== NULL
) {
532 ccptr
->FirstExpressMessage
= newmsg
;
535 findend
= ccptr
->FirstExpressMessage
;
536 while (findend
->next
!= NULL
) {
537 findend
= findend
->next
;
539 findend
->next
= newmsg
;
542 /* If the target context is a session which can handle asynchronous
543 * messages, go ahead and set the flag for that.
545 if (ccptr
->is_async
) {
546 ccptr
->async_waiting
= 1;
547 if (ccptr
->state
== CON_IDLE
) {
548 ccptr
->state
= CON_READY
;
557 * This is the back end to the instant message sending function.
558 * Returns the number of users to which the message was sent.
559 * Sending a zero-length message tests for recipients without sending messages.
561 int send_instant_message(char *lun
, char *lem
, char *x_user
, char *x_msg
)
563 int message_sent
= 0; /* number of successful sends */
564 struct CitContext
*ccptr
;
565 struct ExpressMessage
*newmsg
;
568 int do_send
= 0; /* set to 1 to actually page, not
569 * just check to see if we can.
571 struct savelist
*sl
= NULL
; /* list of rooms to save this page */
572 struct savelist
*sptr
;
573 struct CtdlMessage
*logmsg
= NULL
;
576 if (strlen(x_msg
) > 0) {
577 msglen
= strlen(x_msg
) + 4;
581 /* find the target user's context and append the message */
582 begin_critical_section(S_SESSION_TABLE
);
583 for (ccptr
= ContextList
; ccptr
!= NULL
; ccptr
= ccptr
->next
) {
585 if (ccptr
->fake_username
[0]) {
586 un
= ccptr
->fake_username
;
589 un
= ccptr
->user
.fullname
;
592 if ( ((!strcasecmp(un
, x_user
))
593 || (!strcasecmp(x_user
, "broadcast")))
594 && ((ccptr
->disable_exp
== 0)
595 || (CC
->user
.axlevel
>= 6)) ) {
597 newmsg
= (struct ExpressMessage
*)
598 malloc(sizeof (struct ExpressMessage
));
600 sizeof (struct ExpressMessage
));
601 time(&(newmsg
->timestamp
));
602 safestrncpy(newmsg
->sender
, lun
, sizeof newmsg
->sender
);
603 safestrncpy(newmsg
->sender_email
, lem
, sizeof newmsg
->sender_email
);
604 if (!strcasecmp(x_user
, "broadcast")) {
605 newmsg
->flags
|= EM_BROADCAST
;
607 newmsg
->text
= strdup(x_msg
);
609 add_xmsg_to_context(ccptr
, newmsg
);
613 sptr
= (struct savelist
*)
614 malloc(sizeof(struct savelist
));
616 MailboxName(sptr
->roomname
,
617 sizeof sptr
->roomname
,
618 &ccptr
->user
, PAGELOGROOM
);
625 end_critical_section(S_SESSION_TABLE
);
627 /* Log the page to disk if configured to do so */
628 if ( (do_send
) && (message_sent
) ) {
630 logmsg
= malloc(sizeof(struct CtdlMessage
));
631 memset(logmsg
, 0, sizeof(struct CtdlMessage
));
632 logmsg
->cm_magic
= CTDLMESSAGE_MAGIC
;
633 logmsg
->cm_anon_type
= MES_NORMAL
;
634 logmsg
->cm_format_type
= 0;
635 logmsg
->cm_fields
['A'] = strdup(lun
);
636 logmsg
->cm_fields
['F'] = strdup(lem
);
637 logmsg
->cm_fields
['N'] = strdup(NODENAME
);
638 logmsg
->cm_fields
['O'] = strdup(PAGELOGROOM
);
639 logmsg
->cm_fields
['R'] = strdup(x_user
);
640 logmsg
->cm_fields
['M'] = strdup(x_msg
);
643 /* Save a copy of the message in the sender's log room,
644 * creating the room if necessary.
646 create_room(PAGELOGROOM
, 4, "", 0, 1, 0, VIEW_BBS
);
647 msgnum
= CtdlSubmitMsg(logmsg
, NULL
, PAGELOGROOM
, 0);
649 /* Now save a copy in the global log room, if configured */
650 if (!IsEmptyStr(config
.c_logpages
)) {
651 create_room(config
.c_logpages
, 3, "", 0, 1, 1, VIEW_BBS
);
652 CtdlSaveMsgPointerInRoom(config
.c_logpages
, msgnum
, 0, NULL
);
655 /* Save a copy in each recipient's log room, creating those
656 * rooms if necessary. Note that we create as a type 5 room
657 * rather than 4, which indicates that it's a personal room
658 * but we've already supplied the namespace prefix.
661 create_room(sl
->roomname
, 5, "", 0, 1, 1, VIEW_BBS
);
662 CtdlSaveMsgPointerInRoom(sl
->roomname
, msgnum
, 0, NULL
);
668 CtdlFreeMessage(logmsg
);
671 return (message_sent
);
675 * send instant messages
677 void cmd_sexp(char *argbuf
)
679 int message_sent
= 0;
680 char x_user
[USERNAME_SIZE
];
684 char *x_big_msgbuf
= NULL
;
686 if ((!(CC
->logged_in
)) && (!(CC
->internal_pgm
))) {
687 cprintf("%d Not logged in.\n", ERROR
+ NOT_LOGGED_IN
);
690 if (CC
->fake_username
[0])
691 lun
= CC
->fake_username
;
693 lun
= CC
->user
.fullname
;
695 lem
= CC
->cs_inet_email
;
697 extract_token(x_user
, argbuf
, 0, '|', sizeof x_user
);
698 extract_token(x_msg
, argbuf
, 1, '|', sizeof x_msg
);
701 cprintf("%d You were not previously paged.\n", ERROR
+ NO_SUCH_USER
);
704 if ((!strcasecmp(x_user
, "broadcast")) && (CC
->user
.axlevel
< 6)) {
705 cprintf("%d Higher access required to send a broadcast.\n",
706 ERROR
+ HIGHER_ACCESS_REQUIRED
);
709 /* This loop handles text-transfer pages */
710 if (!strcmp(x_msg
, "-")) {
711 message_sent
= PerformXmsgHooks(lun
, lem
, x_user
, "");
712 if (message_sent
== 0) {
713 if (getuser(NULL
, x_user
))
714 cprintf("%d '%s' does not exist.\n",
715 ERROR
+ NO_SUCH_USER
, x_user
);
717 cprintf("%d '%s' is not logged in "
718 "or is not accepting pages.\n",
719 ERROR
+ RESOURCE_NOT_OPEN
, x_user
);
723 cprintf("%d Transmit message (will deliver to %d users)\n",
724 SEND_LISTING
, message_sent
);
725 x_big_msgbuf
= malloc(SIZ
);
726 memset(x_big_msgbuf
, 0, SIZ
);
727 while (client_getln(x_msg
, sizeof x_msg
) >= 0 && strcmp(x_msg
, "000")) {
728 x_big_msgbuf
= realloc(x_big_msgbuf
,
729 strlen(x_big_msgbuf
) + strlen(x_msg
) + 4);
730 if (!IsEmptyStr(x_big_msgbuf
))
731 if (x_big_msgbuf
[strlen(x_big_msgbuf
)] != '\n')
732 strcat(x_big_msgbuf
, "\n");
733 strcat(x_big_msgbuf
, x_msg
);
735 PerformXmsgHooks(lun
, lem
, x_user
, x_big_msgbuf
);
738 /* This loop handles inline pages */
740 message_sent
= PerformXmsgHooks(lun
, lem
, x_user
, x_msg
);
742 if (message_sent
> 0) {
743 if (!IsEmptyStr(x_msg
))
744 cprintf("%d Message sent", CIT_OK
);
746 cprintf("%d Ok to send message", CIT_OK
);
747 if (message_sent
> 1)
748 cprintf(" to %d users", message_sent
);
751 if (getuser(NULL
, x_user
))
752 cprintf("%d '%s' does not exist.\n",
753 ERROR
+ NO_SUCH_USER
, x_user
);
755 cprintf("%d '%s' is not logged in "
756 "or is not accepting pages.\n",
757 ERROR
+ RESOURCE_NOT_OPEN
, x_user
);
767 * Enter or exit paging-disabled mode
769 void cmd_dexp(char *argbuf
)
773 if (CtdlAccessCheck(ac_logged_in
)) return;
775 new_state
= extract_int(argbuf
, 0);
776 if ((new_state
== 0) || (new_state
== 1)) {
777 CC
->disable_exp
= new_state
;
780 cprintf("%d %d\n", CIT_OK
, CC
->disable_exp
);
785 * Request client termination
787 void cmd_reqt(char *argbuf
) {
788 struct CitContext
*ccptr
;
791 struct ExpressMessage
*newmsg
;
793 if (CtdlAccessCheck(ac_aide
)) return;
794 which_session
= extract_int(argbuf
, 0);
796 begin_critical_section(S_SESSION_TABLE
);
797 for (ccptr
= ContextList
; ccptr
!= NULL
; ccptr
= ccptr
->next
) {
798 if ((ccptr
->cs_pid
== which_session
) || (which_session
== 0)) {
800 newmsg
= (struct ExpressMessage
*)
801 malloc(sizeof (struct ExpressMessage
));
803 sizeof (struct ExpressMessage
));
804 time(&(newmsg
->timestamp
));
805 safestrncpy(newmsg
->sender
, CC
->user
.fullname
,
806 sizeof newmsg
->sender
);
807 newmsg
->flags
|= EM_GO_AWAY
;
808 newmsg
->text
= strdup("Automatic logoff requested.");
810 add_xmsg_to_context(ccptr
, newmsg
);
815 end_critical_section(S_SESSION_TABLE
);
816 cprintf("%d Sent termination request to %d sessions.\n", CIT_OK
, sessions
);
821 CTDL_MODULE_INIT(chat
)
825 CtdlRegisterProtoHook(cmd_chat
, "CHAT", "Begin real-time chat");
826 CtdlRegisterProtoHook(cmd_pexp
, "PEXP", "Poll for instant messages");
827 CtdlRegisterProtoHook(cmd_gexp
, "GEXP", "Get instant messages");
828 CtdlRegisterProtoHook(cmd_sexp
, "SEXP", "Send an instant message");
829 CtdlRegisterProtoHook(cmd_dexp
, "DEXP", "Disable instant messages");
830 CtdlRegisterProtoHook(cmd_reqt
, "REQT", "Request client termination");
831 CtdlRegisterSessionHook(cmd_gexp_async
, EVT_ASYNC
);
832 CtdlRegisterSessionHook(delete_instant_messages
, EVT_STOP
);
833 CtdlRegisterXmsgHook(send_instant_message
, XMSG_PRI_LOCAL
);
836 /* return our Subversion id for the Log */