citadel
About: Citadel is an advanced messaging and collaboration system for groupware and BBS applications (preferred OS: Linux).
  Fossies Dox: citadel.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Loading...
Searching...
No Matches
serv_instmsg.c
Go to the documentation of this file.
1/*
2 * This module handles instant messaging between users.
3 *
4 * Copyright (c) 1987-2022 by the citadel.org team
5 *
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.
8 *
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.
13 */
14
15#include <stdlib.h>
16#include <unistd.h>
17#include <stdio.h>
18#include <fcntl.h>
19#include <signal.h>
20#include <pwd.h>
21#include <errno.h>
22#include <sys/types.h>
23#include <time.h>
24#include <sys/wait.h>
25#include <string.h>
26#include <limits.h>
27#include <libcitadel.h>
28#include "../../sysdep.h"
29#include "../../citadel.h"
30#include "../../server.h"
31#include "../../context.h"
32#include "../../citserver.h"
33#include "../../support.h"
34#include "../../config.h"
35#include "../../msgbase.h"
36#include "../../user_ops.h"
37#include "../../ctdl_module.h"
38#include "serv_instmsg.h"
39
40struct imlog {
41 struct imlog *next;
42 long usernums[2];
43 char usernames[2][128];
44 time_t lastmsg;
46 StrBuf *conversation;
47};
48
49struct imlog *imlist = NULL;
50
51/*
52 * This function handles the logging of instant messages to disk.
53 */
54void log_instant_message(struct CitContext *me, struct CitContext *them, char *msgtext, int serial_number)
55{
56 long usernums[2];
57 long t;
58 struct imlog *iptr = NULL;
59 struct imlog *this_im = NULL;
60
61 memset(usernums, 0, sizeof usernums);
62 usernums[0] = me->user.usernum;
63 usernums[1] = them->user.usernum;
64
65 /* Always put the lower user number first, so we can use the array as a hash value which
66 * represents a pair of users. For a broadcast message one of the users will be 0.
67 */
68 if (usernums[0] > usernums[1]) {
69 t = usernums[0];
70 usernums[0] = usernums[1];
71 usernums[1] = t;
72 }
73
75
76 /* Look for an existing conversation in the hash table.
77 * If not found, create a new one.
78 */
79
80 this_im = NULL;
81 for (iptr = imlist; iptr != NULL; iptr = iptr->next) {
82 if ((iptr->usernums[0] == usernums[0]) && (iptr->usernums[1] == usernums[1])) {
83 /* Existing conversation */
84 this_im = iptr;
85 }
86 }
87 if (this_im == NULL) {
88 /* New conversation */
89 this_im = malloc(sizeof(struct imlog));
90 memset(this_im, 0, sizeof (struct imlog));
91 this_im->usernums[0] = usernums[0];
92 this_im->usernums[1] = usernums[1];
93 /* usernames[] and usernums[] might not be in the same order. This is not an error. */
94 if (me) {
95 safestrncpy(this_im->usernames[0], me->user.fullname, sizeof this_im->usernames[0]);
96 }
97 if (them) {
98 safestrncpy(this_im->usernames[1], them->user.fullname, sizeof this_im->usernames[1]);
99 }
100 this_im->conversation = NewStrBuf();
101 this_im->next = imlist;
102 imlist = this_im;
103 StrBufAppendBufPlain(this_im->conversation, HKEY("<html><body>\r\n"), 0);
104 }
105
106 /* Since it's possible for this function to get called more than once if a user is logged
107 * in on multiple sessions, we use the message's serial number to keep track of whether
108 * we've already logged it.
109 */
110 if (this_im->last_serial != serial_number)
111 {
112 this_im->lastmsg = time(NULL); /* Touch the timestamp so we know when to flush */
113 this_im->last_serial = serial_number;
114 StrBufAppendBufPlain(this_im->conversation, HKEY("<p><b>"), 0);
115 StrBufAppendBufPlain(this_im->conversation, me->user.fullname, -1, 0);
116 StrBufAppendBufPlain(this_im->conversation, HKEY(":</b> "), 0);
117 StrEscAppend(this_im->conversation, NULL, msgtext, 0, 0);
118 StrBufAppendBufPlain(this_im->conversation, HKEY("</p>\r\n"), 0);
119 }
121}
122
123
124/*
125 * Delete any remaining instant messages
126 */
128 struct ExpressMessage *ptr;
129
131 while (CC->FirstExpressMessage != NULL) {
132 ptr = CC->FirstExpressMessage->next;
133 if (CC->FirstExpressMessage->text != NULL)
134 free(CC->FirstExpressMessage->text);
135 free(CC->FirstExpressMessage);
136 CC->FirstExpressMessage = ptr;
137 }
139}
140
141
142/*
143 * Retrieve instant messages
144 */
145void cmd_gexp(char *argbuf) {
146 struct ExpressMessage *ptr;
147
148 if (CC->FirstExpressMessage == NULL) {
149 cprintf("%d No instant messages waiting.\n", ERROR + MESSAGE_NOT_FOUND);
150 return;
151 }
152
154 ptr = CC->FirstExpressMessage;
155 CC->FirstExpressMessage = CC->FirstExpressMessage->next;
157
158 cprintf("%d %d|%ld|%d|%s|%s|%s\n",
160 ((ptr->next != NULL) ? 1 : 0), /* more msgs? */
161 (long)ptr->timestamp, /* time sent */
162 ptr->flags, /* flags */
163 ptr->sender, /* sender of msg */
164 CtdlGetConfigStr("c_nodename"), /* static for now (and possibly deprecated) */
165 ptr->sender_email /* email or jid of sender */
166 );
167
168 if (ptr->text != NULL) {
169 memfmout(ptr->text, "\n");
170 free(ptr->text);
171 }
172
173 cprintf("000\n");
174 free(ptr);
175}
176
177
178/*
179 * Asynchronously deliver instant messages
180 */
181void cmd_gexp_async(void) {
182
183 /* Only do this if the session can handle asynchronous protocol */
184 if (CC->is_async == 0) return;
185
186 /* And don't do it if there's nothing to send. */
187 if (CC->FirstExpressMessage == NULL) return;
188
189 cprintf("%d instant msg\n", ASYNC_MSG + ASYNC_GEXP);
190}
191
192
193/*
194 * Back end support function for send_instant_message() and company
195 */
196void add_xmsg_to_context(struct CitContext *ccptr, struct ExpressMessage *newmsg)
197{
198 struct ExpressMessage *findend;
199
200 if (ccptr->FirstExpressMessage == NULL) {
201 ccptr->FirstExpressMessage = newmsg;
202 }
203 else {
204 findend = ccptr->FirstExpressMessage;
205 while (findend->next != NULL) {
206 findend = findend->next;
207 }
208 findend->next = newmsg;
209 }
210
211 /* If the target context is a session which can handle asynchronous
212 * messages, go ahead and set the flag for that.
213 */
214 set_async_waiting(ccptr);
215}
216
217
218/*
219 * This is the back end to the instant message sending function.
220 * Returns the number of users to which the message was sent.
221 * Sending a zero-length message tests for recipients without sending messages.
222 */
223int send_instant_message(char *lun, char *lem, char *x_user, char *x_msg)
224{
225 int message_sent = 0; /* number of successful sends */
226 struct CitContext *ccptr;
227 struct ExpressMessage *newmsg = NULL;
228 int do_send = 0; /* 1 = send message; 0 = only check for valid recipient */
229 static int serial_number = 0; /* this keeps messages from getting logged twice */
230
231 if (!IsEmptyStr(x_msg)) {
232 do_send = 1;
233 }
234
235 /* find the target user's context and append the message */
237 ++serial_number;
238 for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
239
240 if ( ((!strcasecmp(ccptr->user.fullname, x_user))
241 || (!strcasecmp(x_user, "broadcast")))
242 && (ccptr->can_receive_im)
243 && ((ccptr->disable_exp == 0)
244 || (CC->user.axlevel >= AxAideU)) ) {
245 if (do_send) {
246 newmsg = (struct ExpressMessage *) malloc(sizeof (struct ExpressMessage));
247 memset(newmsg, 0, sizeof (struct ExpressMessage));
248 time(&(newmsg->timestamp));
249 safestrncpy(newmsg->sender, lun, sizeof newmsg->sender);
250 safestrncpy(newmsg->sender_email, lem, sizeof newmsg->sender_email);
251 if (!strcasecmp(x_user, "broadcast")) {
252 newmsg->flags |= EM_BROADCAST;
253 }
254 newmsg->text = strdup(x_msg);
255
256 add_xmsg_to_context(ccptr, newmsg);
257
258 /* and log it ... */
259 if (ccptr != CC) {
260 log_instant_message(CC, ccptr, newmsg->text, serial_number);
261 }
262 }
263 ++message_sent;
264 }
265 }
267 return (message_sent);
268}
269
270
271/*
272 * send instant messages
273 */
274void cmd_sexp(char *argbuf)
275{
276 int message_sent = 0;
277 char x_user[USERNAME_SIZE];
278 char x_msg[1024];
279 char *lem;
280 char *x_big_msgbuf = NULL;
281
282 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
283 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
284 return;
285 }
286
287 lem = CC->cs_principal_id;
288
289 extract_token(x_user, argbuf, 0, '|', sizeof x_user);
290 extract_token(x_msg, argbuf, 1, '|', sizeof x_msg);
291
292 if (!x_user[0]) {
293 cprintf("%d You were not previously paged.\n", ERROR + NO_SUCH_USER);
294 return;
295 }
296 if ((!strcasecmp(x_user, "broadcast")) && (CC->user.axlevel < AxAideU)) {
297 cprintf("%d Higher access required to send a broadcast.\n",
299 return;
300 }
301 /* This loop handles text-transfer pages */
302 if (!strcmp(x_msg, "-")) {
303 message_sent = PerformXmsgHooks(CC->user.fullname, lem, x_user, "");
304 if (message_sent == 0) {
305 if (CtdlGetUser(NULL, x_user))
306 cprintf("%d '%s' does not exist.\n",
307 ERROR + NO_SUCH_USER, x_user);
308 else
309 cprintf("%d '%s' is not logged in "
310 "or is not accepting pages.\n",
311 ERROR + RESOURCE_NOT_OPEN, x_user);
312 return;
313 }
315 cprintf("%d Transmit message (will deliver to %d users)\n",
316 SEND_LISTING, message_sent);
317 x_big_msgbuf = malloc(SIZ);
318 memset(x_big_msgbuf, 0, SIZ);
319 while (client_getln(x_msg, sizeof x_msg) >= 0 && strcmp(x_msg, "000")) {
320 x_big_msgbuf = realloc(x_big_msgbuf,
321 strlen(x_big_msgbuf) + strlen(x_msg) + 4);
322 if (!IsEmptyStr(x_big_msgbuf))
323 if (x_big_msgbuf[strlen(x_big_msgbuf)] != '\n')
324 strcat(x_big_msgbuf, "\n");
325 strcat(x_big_msgbuf, x_msg);
326 }
327 PerformXmsgHooks(CC->user.fullname, lem, x_user, x_big_msgbuf);
328 free(x_big_msgbuf);
329
330 /* This loop handles inline pages */
331 } else {
332 message_sent = PerformXmsgHooks(CC->user.fullname, lem, x_user, x_msg);
333
334 if (message_sent > 0) {
335 if (!IsEmptyStr(x_msg)) {
336 cprintf("%d Message sent", CIT_OK);
337 }
338 else {
339 cprintf("%d Ok to send message", CIT_OK);
340 }
341 if (message_sent > 1) {
342 cprintf(" to %d users", message_sent);
343 }
344 cprintf(".\n");
345 } else {
346 if (CtdlGetUser(NULL, x_user)) {
347 cprintf("%d '%s' does not exist.\n", ERROR + NO_SUCH_USER, x_user);
348 }
349 else {
350 cprintf("%d '%s' is not logged in or is not accepting instant messages.\n",
351 ERROR + RESOURCE_NOT_OPEN, x_user);
352 }
353 }
354
355
356 }
357}
358
359
360/*
361 * Enter or exit paging-disabled mode
362 */
363void cmd_dexp(char *argbuf)
364{
365 int new_state;
366
367 if (CtdlAccessCheck(ac_logged_in)) return;
368
369 new_state = extract_int(argbuf, 0);
370 if ((new_state == 0) || (new_state == 1)) {
371 CC->disable_exp = new_state;
372 }
373
374 cprintf("%d %d\n", CIT_OK, CC->disable_exp);
375}
376
377
378/*
379 * Request client termination
380 */
381void cmd_reqt(char *argbuf) {
382 struct CitContext *ccptr;
383 int sessions = 0;
384 int which_session;
385 struct ExpressMessage *newmsg;
386
387 if (CtdlAccessCheck(ac_aide)) return;
388 which_session = extract_int(argbuf, 0);
389
391 for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
392 if ((ccptr->cs_pid == which_session) || (which_session == 0)) {
393
394 newmsg = (struct ExpressMessage *)
395 malloc(sizeof (struct ExpressMessage));
396 memset(newmsg, 0,
397 sizeof (struct ExpressMessage));
398 time(&(newmsg->timestamp));
399 safestrncpy(newmsg->sender, CC->user.fullname,
400 sizeof newmsg->sender);
401 newmsg->flags |= EM_GO_AWAY;
402 newmsg->text = strdup("Automatic logoff requested.");
403
404 add_xmsg_to_context(ccptr, newmsg);
405 ++sessions;
406
407 }
408 }
410 cprintf("%d Sent termination request to %d sessions.\n", CIT_OK, sessions);
411}
412
413
414/*
415 * This is the back end for flush_conversations_to_disk()
416 * At this point we've isolated a single conversation (struct imlog)
417 * and are ready to write it to disk.
418 */
420 struct CtdlMessage *msg;
421 long msgnum = 0;
422 char roomname[ROOMNAMELEN];
423 StrBuf *MsgBuf, *FullMsgBuf;
424
425 StrBufAppendBufPlain(im->conversation, HKEY(
426 "</body>\r\n"
427 "</html>\r\n"
428 ), 0
429 );
430
431 MsgBuf = StrBufRFC2047encodeMessage(im->conversation);
432 FlushStrBuf(im->conversation);
433 FullMsgBuf = NewStrBufPlain(NULL, StrLength(im->conversation) + 100);
434
435 StrBufAppendBufPlain(FullMsgBuf, HKEY(
436 "Content-type: text/html; charset=UTF-8\r\n"
437 "Content-Transfer-Encoding: quoted-printable\r\n"
438 "\r\n"
439 ), 0
440 );
441 StrBufAppendBuf (FullMsgBuf, MsgBuf, 0);
442 FreeStrBuf(&MsgBuf);
443
444 msg = malloc(sizeof(struct CtdlMessage));
445 memset(msg, 0, sizeof(struct CtdlMessage));
449 if (!IsEmptyStr(im->usernames[0])) {
450 CM_SetField(msg, eAuthor, im->usernames[0], strlen(im->usernames[0]));
451 } else {
452 CM_SetField(msg, eAuthor, HKEY("Citadel"));
453 }
454 if (!IsEmptyStr(im->usernames[1])) {
455 CM_SetField(msg, eRecipient, im->usernames[1], strlen(im->usernames[1]));
456 }
457
459 CM_SetAsFieldSB(msg, eMesageText, &FullMsgBuf); /* we own this memory now */
460
461 /* Start with usernums[1] because it's guaranteed to be higher than usernums[0],
462 * so if there's only one party, usernums[0] will be zero but usernums[1] won't.
463 * Create the room if necessary. Note that we create as a type 5 room rather
464 * than 4, which indicates that it's a personal room but we've already supplied
465 * the namespace prefix.
466 *
467 * In the unlikely event that usernums[1] is zero, a room with an invalid namespace
468 * prefix will be created. That's ok because the auto-purger will clean it up later.
469 */
470 snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[1], PAGELOGROOM);
471 CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS);
472 msgnum = CtdlSubmitMsg(msg, NULL, roomname);
473 CM_Free(msg);
474
475 /* If there is a valid user number in usernums[0], save a copy for them too. */
476 if (im->usernums[0] > 0) {
477 snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[0], PAGELOGROOM);
478 CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS);
479 CtdlSaveMsgPointerInRoom(roomname, msgnum, 0, NULL);
480 }
481
482 /* Finally, if we're logging instant messages globally, do that now. */
483 if (!IsEmptyStr(CtdlGetConfigStr("c_logpages"))) {
484 CtdlCreateRoom(CtdlGetConfigStr("c_logpages"), 3, "", 0, 1, 1, VIEW_BBS);
485 CtdlSaveMsgPointerInRoom(CtdlGetConfigStr("c_logpages"), msgnum, 0, NULL);
486 }
487
488}
489
490/*
491 * Locate instant message conversations which have gone idle
492 * (or, if the server is shutting down, locate *all* conversations)
493 * and flush them to disk (in the participants' log rooms, etc.)
494 */
495void flush_conversations_to_disk(time_t if_older_than) {
496
497 struct imlog *flush_these = NULL;
498 struct imlog *dont_flush_these = NULL;
499 struct imlog *imptr = NULL;
500 struct CitContext *nptr;
501 int nContexts, i;
502
503 nptr = CtdlGetContextArray(&nContexts) ; /* Make a copy of the current wholist */
504
506 while (imlist)
507 {
508 imptr = imlist;
509 imlist = imlist->next;
510
511 /* For a two party conversation, if one party has logged out, force flush. */
512 if (nptr) {
513 int user0_is_still_online = 0;
514 int user1_is_still_online = 0;
515 for (i=0; i<nContexts; i++) {
516 if (nptr[i].user.usernum == imptr->usernums[0]) ++user0_is_still_online;
517 if (nptr[i].user.usernum == imptr->usernums[1]) ++user1_is_still_online;
518 }
519 if (imptr->usernums[0] != imptr->usernums[1]) { /* two party conversation */
520 if ((!user0_is_still_online) || (!user1_is_still_online)) {
521 imptr->lastmsg = 0L; /* force flush */
522 }
523 }
524 else { /* one party conversation (yes, people do IM themselves) */
525 if (!user0_is_still_online) {
526 imptr->lastmsg = 0L; /* force flush */
527 }
528 }
529 }
530
531 /* Now test this conversation to see if it qualifies for flushing. */
532 if ((time(NULL) - imptr->lastmsg) > if_older_than)
533 {
534 /* This conversation qualifies. Move it to the list of ones to flush. */
535 imptr->next = flush_these;
536 flush_these = imptr;
537 }
538 else {
539 /* Move it to the list of ones not to flush. */
540 imptr->next = dont_flush_these;
541 dont_flush_these = imptr;
542 }
543 }
544 imlist = dont_flush_these;
546 free(nptr);
547
548 /* We are now outside of the critical section, and we are the only thread holding a
549 * pointer to a linked list of conversations to be flushed to disk.
550 */
551 while (flush_these) {
552
553 flush_individual_conversation(flush_these); /* This will free the string buffer */
554 imptr = flush_these;
555 flush_these = flush_these->next;
556 free(imptr);
557 }
558}
559
560
561void instmsg_timer(void) {
562 flush_conversations_to_disk(300); /* Anything that hasn't peeped in more than 5 minutes */
563}
564
565
567 flush_conversations_to_disk(0); /* Get it ALL onto disk NOW. */
568}
569
570
571// Initialization function, called from modules_init.c
573 if (!threading) {
574 CtdlRegisterProtoHook(cmd_gexp, "GEXP", "Get instant messages");
575 CtdlRegisterProtoHook(cmd_sexp, "SEXP", "Send an instant message");
576 CtdlRegisterProtoHook(cmd_dexp, "DEXP", "Disable instant messages");
577 CtdlRegisterProtoHook(cmd_reqt, "REQT", "Request client termination");
583 }
584
585 /* return our module name for the log */
586 return "instmsg";
587}
#define ROOMNAMELEN
Definition: citadel.h:51
#define MES_NORMAL
Definition: citadel.h:118
#define USERNAME_SIZE
Definition: citadel.h:52
char * CtdlGetConfigStr(char *key)
Definition: config.c:363
CitContext * ContextList
Definition: context.c:21
void set_async_waiting(struct CitContext *ccptr)
Definition: context.c:669
CitContext * CtdlGetContextArray(int *count)
Definition: context.c:423
#define CC
Definition: context.h:140
int CtdlAccessCheck(int)
Definition: user_ops.c:334
#define PRIO_SHUTDOWN
Definition: ctdl_module.h:94
void CtdlRegisterSessionHook(void(*fcn_ptr)(void), int EventType, int Priority)
int threading
Definition: modules_init.c:24
#define PRIO_ASYNC
Definition: ctdl_module.h:92
#define PRIO_CLEANUP
Definition: ctdl_module.h:78
@ ac_aide
Definition: ctdl_module.h:249
@ ac_logged_in
Definition: ctdl_module.h:247
unsigned CtdlCreateRoom(char *new_room_name, int new_room_type, char *new_room_pass, int new_room_floor, int really_create, int avoid_access, int new_room_view)
Definition: room_ops.c:1035
void CtdlRegisterXmsgHook(int(*fcn_ptr)(char *, char *, char *, char *), int order)
int CtdlGetUser(struct ctdluser *usbuf, char *name)
Definition: user_ops.c:64
void CtdlRegisterProtoHook(void(*handler)(char *), char *cmd, char *desc)
#define PRIO_STOP
Definition: ctdl_module.h:90
#define ASYNC_GEXP
Definition: ipcdef.h:37
#define CIT_OK
Definition: ipcdef.h:6
#define LISTING_FOLLOWS
Definition: ipcdef.h:5
#define HIGHER_ACCESS_REQUIRED
Definition: ipcdef.h:23
#define ASYNC_MSG
Definition: ipcdef.h:36
#define NOT_LOGGED_IN
Definition: ipcdef.h:17
#define ERROR
Definition: ipcdef.h:9
#define SEND_LISTING
Definition: ipcdef.h:8
#define MESSAGE_NOT_FOUND
Definition: ipcdef.h:34
#define NO_SUCH_USER
Definition: ipcdef.h:29
#define RESOURCE_NOT_OPEN
Definition: ipcdef.h:26
void CM_Free(struct CtdlMessage *msg)
Definition: msgbase.c:305
int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check, struct CtdlMessage *supplied_msg)
Definition: msgbase.c:2384
long CtdlSubmitMsg(struct CtdlMessage *msg, struct recptypes *recps, const char *force)
Definition: msgbase.c:2588
void CM_SetField(struct CtdlMessage *Msg, eMsgField which, const char *buf, long length)
Definition: msgbase.c:137
void memfmout(char *mptr, const char *nl)
Definition: msgbase.c:855
void CM_SetAsFieldSB(struct CtdlMessage *Msg, eMsgField which, StrBuf **buf)
Definition: msgbase.c:253
void * malloc(unsigned)
void free(void *)
int PerformXmsgHooks(char *sender, char *sender_email, char *recp, char *msg)
int send_instant_message(char *lun, char *lem, char *x_user, char *x_msg)
Definition: serv_instmsg.c:223
void add_xmsg_to_context(struct CitContext *ccptr, struct ExpressMessage *newmsg)
Definition: serv_instmsg.c:196
void cmd_gexp_async(void)
Definition: serv_instmsg.c:181
void flush_individual_conversation(struct imlog *im)
Definition: serv_instmsg.c:419
void cmd_dexp(char *argbuf)
Definition: serv_instmsg.c:363
void cmd_sexp(char *argbuf)
Definition: serv_instmsg.c:274
void log_instant_message(struct CitContext *me, struct CitContext *them, char *msgtext, int serial_number)
Definition: serv_instmsg.c:54
void cmd_gexp(char *argbuf)
Definition: serv_instmsg.c:145
void cmd_reqt(char *argbuf)
Definition: serv_instmsg.c:381
void delete_instant_messages(void)
Definition: serv_instmsg.c:127
char * ctdl_module_init_instmsg(void)
Definition: serv_instmsg.c:572
void instmsg_timer(void)
Definition: serv_instmsg.c:561
void instmsg_shutdown(void)
Definition: serv_instmsg.c:566
void flush_conversations_to_disk(time_t if_older_than)
Definition: serv_instmsg.c:495
struct imlog * imlist
Definition: serv_instmsg.c:49
#define EVT_STOP
Definition: server.h:212
#define CTDLMESSAGE_MAGIC
Definition: server.h:42
@ eMesageText
Definition: server.h:315
@ eAuthor
Definition: server.h:307
@ eRecipient
Definition: server.h:318
@ eOriginalRoom
Definition: server.h:316
#define FMT_RFC822
Definition: server.h:178
#define EM_BROADCAST
Definition: server.h:126
#define EVT_ASYNC
Definition: server.h:220
@ S_IM_LOGS
Definition: server.h:156
@ S_SESSION_TABLE
Definition: server.h:136
#define EVT_TIMER
Definition: server.h:224
@ XMSG_PRI_LOCAL
Definition: server.h:237
#define EM_GO_AWAY
Definition: server.h:127
#define EVT_SHUTDOWN
Definition: server.h:226
int cs_pid
Definition: context.h:44
struct ctdluser user
Definition: context.h:109
CitContext * next
Definition: context.h:42
int can_receive_im
Definition: context.h:81
struct ExpressMessage * FirstExpressMessage
Definition: context.h:113
int disable_exp
Definition: context.h:114
int cm_magic
Definition: server.h:34
char cm_anon_type
Definition: server.h:35
char cm_format_type
Definition: server.h:36
unsigned flags
Definition: server.h:120
char sender_email[256]
Definition: server.h:122
struct ExpressMessage * next
Definition: server.h:118
char * text
Definition: server.h:123
time_t timestamp
Definition: server.h:119
char sender[256]
Definition: server.h:121
char fullname[64]
Definition: citadel.h:80
long usernum
Definition: citadel.h:77
StrBuf * conversation
Definition: serv_instmsg.c:46
char usernames[2][128]
Definition: serv_instmsg.c:43
struct imlog * next
Definition: serv_instmsg.c:41
long usernums[2]
Definition: serv_instmsg.c:42
int last_serial
Definition: serv_instmsg.c:45
time_t lastmsg
Definition: serv_instmsg.c:44
#define PAGELOGROOM
Definition: sysconfig.h:71
#define SIZ
Definition: sysconfig.h:33
void cprintf(const char *format,...)
Definition: sysdep.c:369
int client_getln(char *buf, int bufsize)
Definition: sysdep.c:528
void unbuffer_output(void)
Definition: sysdep.c:268
void begin_critical_section(int which_one)
Definition: threads.c:62
void end_critical_section(int which_one)
Definition: threads.c:80