* Began (but did not finish) applying GPL3+ declarations to each source file. This...
[citadel.git] / citadel / client_chat.c
1 /*
2  * $Id$
3  *
4  * front end for chat mode
5  * (the "single process" version - no more fork() anymore)
6  *
7  * Copyright (c) 1987-2009 by the citadel.org team
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 3 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "sysdep.h"
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <errno.h>
33
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
36 # include <time.h>
37 #else
38 # if HAVE_SYS_TIME_H
39 #  include <sys/time.h>
40 # else
41 #  include <time.h>
42 # endif
43 #endif
44
45 #include <sys/types.h>
46 #ifdef HAVE_SYS_SELECT_H
47 #include <sys/select.h>
48 #endif
49 #include <stdarg.h>
50 #include <libcitadel.h>
51 #include "citadel.h"
52 #include "citadel_ipc.h"
53 #include "client_chat.h"
54 #include "commands.h"
55 #include "routines.h"
56 #include "citadel_decls.h"
57 #include "rooms.h"
58 #include "messages.h"
59 #ifndef HAVE_SNPRINTF
60 #include "snprintf.h"
61 #endif
62 #include "screen.h"
63
64 #define MIN(a, b) ((a) < (b) ? (a) : (b))
65
66 extern char temp[];
67 void ctdl_getline(char *, int);
68
69 char last_paged[SIZ] = "";
70
71 void chatmode(CtdlIPC *ipc)
72 {
73         char wbuf[SIZ];
74         char buf[SIZ];
75         char c_user[SIZ];
76         char c_text[SIZ];
77         char c_room[SIZ];
78         char last_user[SIZ];
79         int send_complete_line;
80         int recv_complete_line;
81         char ch;
82         int a, pos;
83         time_t last_transmit;
84
85         fd_set rfds;
86         struct timeval tv;
87         int retval;
88
89         CtdlIPC_chat_send(ipc, "CHAT");
90         CtdlIPC_chat_recv(ipc, buf);
91         if (buf[0] != '8') {
92                 scr_printf("%s\n", &buf[4]);
93                 return;
94         }
95         scr_printf("Entering chat mode "
96                 "(type /quit to exit, /help for other cmds)\n");
97         set_keepalives(KA_NO);
98         last_transmit = time(NULL);
99
100         strcpy(buf, "");
101         strcpy(wbuf, "");
102         strcpy(last_user, ""); 
103         color(BRIGHT_YELLOW);
104         sln_printf_if("\n");
105         sln_printf("> ");
106         send_complete_line = 0;
107         recv_complete_line = 0;
108
109         while (1) {
110                 sln_flush();
111                 FD_ZERO(&rfds);
112                 FD_SET(0, &rfds);
113                 FD_SET(CtdlIPC_getsockfd(ipc), &rfds);
114                 tv.tv_sec = S_KEEPALIVE;
115                 tv.tv_usec = 0;
116                 retval = select(CtdlIPC_getsockfd(ipc) + 1, &rfds,
117                         NULL, NULL, &tv);
118
119                 /* If there's data from the server... */
120                 if (FD_ISSET(CtdlIPC_getsockfd(ipc), &rfds)) {
121                         CtdlIPC_chat_recv(ipc, buf);
122                         recv_complete_line = 1;
123                         goto RCL;       /* ugly, but we've gotta get out! */
124                 }
125
126                 /* If there's data from the keyboard... */
127                 if (FD_ISSET(0, &rfds)) {
128                         ch = scr_getc(SCR_BLOCK);
129                         if ((ch == 10) || (ch == 13)) {
130                                 send_complete_line = 1;
131                         } else if ((ch == 8) || (ch == 127)) {
132                                 if (!IsEmptyStr(wbuf)) {
133                                         wbuf[strlen(wbuf) - 1] = 0;
134                                         sln_printf("%c %c", 8, 8);
135                                 }
136                         } else {
137                                 sln_putc(ch);
138                                 wbuf[strlen(wbuf) + 1] = 0;
139                                 wbuf[strlen(wbuf)] = ch;
140                         }
141                 }
142
143                 /* if the user hit return, send the line */
144 RCL:            if (send_complete_line) {
145                         CtdlIPC_chat_send(ipc, wbuf);
146                         last_transmit = time(NULL);
147                         strcpy(wbuf, "");
148                         send_complete_line = 0;
149                 }
150
151                 /* if it's time to word wrap, send a partial line */
152                 if (strlen(wbuf) >= (77 - strlen(fullname))) {
153                         pos = 0;
154                         for (a = 0; !IsEmptyStr(&wbuf[a]); ++a) {
155                                 if (wbuf[a] == 32)
156                                         pos = a;
157                         }
158                         if (pos == 0) {
159                                 CtdlIPC_chat_send(ipc, wbuf);
160                                 last_transmit = time(NULL);
161                                 strcpy(wbuf, "");
162                                 send_complete_line = 0;
163                         } else {
164                                 wbuf[pos] = 0;
165                                 CtdlIPC_chat_send(ipc, wbuf);
166                                 last_transmit = time(NULL);
167                                 strcpy(wbuf, &wbuf[pos + 1]);
168                         }
169                 }
170
171                 if (recv_complete_line) {
172                         sln_printf("\r%79s\r", "");
173                         if (!strcmp(buf, "000")) {
174                                 color(BRIGHT_WHITE);
175                                 sln_printf("\rExiting chat mode\n");
176                                 sln_flush();
177                                 set_keepalives(KA_YES);
178
179                                 /* Some users complained about the client and
180                                  * server losing protocol synchronization when
181                                  * exiting chat.  This little dialog forces
182                                  * everything to be hunky-dory.
183                                  */
184                                 CtdlIPC_chat_send(ipc, "ECHO __ExitingChat__");
185                                 do {
186                                         CtdlIPC_chat_recv(ipc, buf);
187                                 } while (strcmp(buf, "200 __ExitingChat__"));
188                                 return;
189                         }
190                         if (num_parms(buf) >= 2) {
191                                 extract_token(c_user, buf, 0, '|', sizeof c_user);
192                                 extract_token(c_text, buf, 1, '|', sizeof c_text);
193                                 if (num_parms(buf) > 2) {
194                                         extract_token(c_room, buf, 2, '|', sizeof c_room);
195                                         scr_printf("Got room %s\n", c_room);
196                                 }
197                                 if (strcasecmp(c_text, "NOOP")) {
198                                         if (!strcmp(c_user, fullname)) {
199                                                 color(BRIGHT_YELLOW);
200                                         } else if (!strcmp(c_user, ":")) {
201                                                 color(BRIGHT_RED);
202                                         } else {
203                                                 color(BRIGHT_GREEN);
204                                         }
205                                         if (strcmp(c_user, last_user)) {
206                                                 snprintf(buf, sizeof buf, "%s: %s", c_user, c_text);
207                                         } else {
208                                                 size_t i = MIN(sizeof buf - 1,
209                                                      strlen(c_user) + 2);
210
211                                                 memset(buf, ' ', i);
212                                                 safestrncpy(&buf[i], c_text,
213                                                          sizeof buf - i);
214                                         }
215                                         while (strlen(buf) < 79)
216                                                 strcat(buf, " ");
217                                         if (strcmp(c_user, last_user)) {
218                                                 sln_printf("\r%79s\n", "");
219                                                 strcpy(last_user, c_user);
220                                         }
221                                         scr_printf("\r%s\n", buf);
222                                         scr_flush();
223                                 }
224                         }
225                         color(BRIGHT_YELLOW);
226                         sln_printf("\r> %s", wbuf);
227                         sln_flush();
228                         recv_complete_line = 0;
229                         strcpy(buf, "");
230                 }
231
232                 /* If the user is sitting idle, send a half-keepalive to the
233                  * server to prevent session timeout.
234                  */
235                 if ((time(NULL) - last_transmit) >= S_KEEPALIVE) {
236                         CtdlIPC_chat_send(ipc, "NOOP");
237                         last_transmit = time(NULL);
238                 }
239
240         }
241 }
242
243 /*
244  * send an instant message
245  */
246 void page_user(CtdlIPC *ipc)
247 {
248         char buf[SIZ], touser[SIZ], msg[SIZ];
249         FILE *pagefp;
250
251         strcpy(touser, last_paged);
252         strprompt("Page who", touser, 30);
253
254         /* old server -- use inline paging */
255         if (ipc->ServInfo.paging_level == 0) {
256                 newprompt("Message: ", msg, 69);
257                 snprintf(buf, sizeof buf, "SEXP %s|%s", touser, msg);
258                 CtdlIPC_chat_send(ipc, buf);
259                 CtdlIPC_chat_recv(ipc, buf);
260                 if (!strncmp(buf, "200", 3)) {
261                         strcpy(last_paged, touser);
262                 }
263                 scr_printf("%s\n", &buf[4]);
264                 return;
265         }
266         /* new server -- use extended paging */
267         else if (ipc->ServInfo.paging_level >= 1) {
268                 snprintf(buf, sizeof buf, "SEXP %s||", touser);
269                 CtdlIPC_chat_send(ipc, buf);
270                 CtdlIPC_chat_recv(ipc, buf);
271                 if (buf[0] != '2') {
272                         scr_printf("%s\n", &buf[4]);
273                         return;
274                 }
275                 if (client_make_message(ipc, temp, touser, 0, 0, 0, NULL, 0) != 0) {
276                         scr_printf("No message sent.\n");
277                         return;
278                 }
279                 pagefp = fopen(temp, "r");
280                 unlink(temp);
281                 snprintf(buf, sizeof buf, "SEXP %s|-", touser);
282                 CtdlIPC_chat_send(ipc, buf);
283                 CtdlIPC_chat_recv(ipc, buf);
284                 if (buf[0] == '4') {
285                         strcpy(last_paged, touser);
286                         while (fgets(buf, sizeof buf, pagefp) != NULL) {
287                                 buf[strlen(buf) - 1] = 0;
288                                 CtdlIPC_chat_send(ipc, buf);
289                         }
290                         fclose(pagefp);
291                         CtdlIPC_chat_send(ipc, "000");
292                         scr_printf("Message sent.\n");
293                 } else {
294                         scr_printf("%s\n", &buf[4]);
295                 }
296         }
297 }
298
299
300 void quiet_mode(CtdlIPC *ipc)
301 {
302         static int quiet = 0;
303         char cret[SIZ];
304         int r;
305
306         r = CtdlIPCEnableInstantMessageReceipt(ipc, !quiet, cret);
307         if (r / 100 == 2) {
308                 quiet = !quiet;
309                 scr_printf("Quiet mode %sabled (%sother users may page you)\n",
310                                 (quiet) ? "en" : "dis",
311                                 (quiet) ? "no " : "");
312         } else {
313                 scr_printf("Unable to change quiet mode: %s\n", cret);
314         }
315 }
316
317
318 void stealth_mode(CtdlIPC *ipc)
319 {
320         static int stealth = 0;
321         char cret[SIZ];
322         int r;
323
324         r = CtdlIPCStealthMode(ipc, !stealth, cret);
325         if (r / 100 == 2) {
326                 stealth = !stealth;
327                 scr_printf("Stealth mode %sabled (you are %s)\n",
328                                 (stealth) ? "en" : "dis",
329                                 (stealth) ? "invisible" : "listed as online");
330         } else {
331                 scr_printf("Unable to change stealth mode: %s\n", cret);
332         }
333 }