1 /* HexChat
2 * Copyright (C) 1998-2010 Peter Zelezny.
3 * Copyright (C) 2009-2013 Berke Viktor.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 /* per-channel/dialog settings :: /CHANOPT */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <errno.h>
29
30 #ifdef WIN32
31 #include <io.h>
32 #else
33 #include <unistd.h>
34 #endif
35
36 #include "hexchat.h"
37
38 #include "cfgfiles.h"
39 #include "server.h"
40 #include "text.h"
41 #include "util.h"
42 #include "hexchatc.h"
43
44
45 static GSList *chanopt_list = NULL;
46 static gboolean chanopt_open = FALSE;
47 static gboolean chanopt_changed = FALSE;
48
49
50 typedef struct
51 {
52 char *name;
53 char *alias; /* old names from 2.8.4 */
54 int offset;
55 } channel_options;
56
57 #define S_F(xx) STRUCT_OFFSET_STR(struct session,xx)
58
59 static const channel_options chanopt[] =
60 {
61 {"alert_balloon", NULL, S_F(alert_balloon)},
62 {"alert_beep", "BEEP", S_F(alert_beep)},
63 {"alert_taskbar", NULL, S_F(alert_taskbar)},
64 {"alert_tray", "TRAY", S_F(alert_tray)},
65
66 {"text_hidejoinpart", "CONFMODE", S_F(text_hidejoinpart)},
67 {"text_logging", NULL, S_F(text_logging)},
68 {"text_scrollback", NULL, S_F(text_scrollback)},
69 {"text_strip", NULL, S_F(text_strip)},
70 };
71
72 #undef S_F
73
74 static char *
chanopt_value(guint8 val)75 chanopt_value (guint8 val)
76 {
77 switch (val)
78 {
79 case SET_OFF:
80 return _("OFF");
81 case SET_ON:
82 return _("ON");
83 case SET_DEFAULT:
84 return _("{unset}");
85 default:
86 g_assert_not_reached ();
87 return NULL;
88 }
89 }
90
91 static guint8
str_to_chanopt(const char * str)92 str_to_chanopt (const char *str)
93 {
94 if (!g_ascii_strcasecmp (str, "ON") || !strcmp (str, "1"))
95 return SET_ON;
96 else if (!g_ascii_strcasecmp (str, "OFF") || !strcmp (str, "0"))
97 return SET_OFF;
98 else
99 return SET_DEFAULT;
100 }
101
102 /* handle the /CHANOPT command */
103
104 int
chanopt_command(session * sess,char * tbuf,char * word[],char * word_eol[])105 chanopt_command (session *sess, char *tbuf, char *word[], char *word_eol[])
106 {
107 int dots, i = 0, j, p = 0;
108 guint8 val;
109 int offset = 2;
110 char *find;
111 gboolean quiet = FALSE;
112 int newval = -1;
113
114 if (!strcmp (word[2], "-quiet"))
115 {
116 quiet = TRUE;
117 offset++;
118 }
119
120 find = word[offset++];
121
122 if (word[offset][0])
123 {
124 newval = str_to_chanopt (word[offset]);
125 }
126
127 if (!quiet)
128 PrintTextf (sess, "\002%s\002: %s \002%s\002: %s\n",
129 _("Network"),
130 sess->server->network ? server_get_network (sess->server, TRUE) : _("<none>"),
131 _("Channel"),
132 sess->session_name[0] ? sess->session_name : _("<none>"));
133
134 while (i < sizeof (chanopt) / sizeof (channel_options))
135 {
136 if (find[0] == 0 || match (find, chanopt[i].name) || (chanopt[i].alias && match (find, chanopt[i].alias)))
137 {
138 if (newval != -1) /* set new value */
139 {
140 *(guint8 *)G_STRUCT_MEMBER_P(sess, chanopt[i].offset) = newval;
141 chanopt_changed = TRUE;
142 }
143
144 if (!quiet) /* print value */
145 {
146 strcpy (tbuf, chanopt[i].name);
147 p = strlen (tbuf);
148
149 tbuf[p++] = 3;
150 tbuf[p++] = '2';
151
152 dots = 20 - strlen (chanopt[i].name);
153
154 for (j = 0; j < dots; j++)
155 tbuf[p++] = '.';
156 tbuf[p++] = 0;
157
158 val = G_STRUCT_MEMBER (guint8, sess, chanopt[i].offset);
159 PrintTextf (sess, "%s\0033:\017 %s", tbuf, chanopt_value (val));
160 }
161 }
162 i++;
163 }
164
165 return TRUE;
166 }
167
168 /* is a per-channel setting set? Or is it UNSET and
169 * the global version is set? */
170
171 gboolean
chanopt_is_set(unsigned int global,guint8 per_chan_setting)172 chanopt_is_set (unsigned int global, guint8 per_chan_setting)
173 {
174 if (per_chan_setting == SET_ON || per_chan_setting == SET_OFF)
175 return per_chan_setting;
176 else
177 return global;
178 }
179
180 /* === below is LOADING/SAVING stuff only === */
181
182 typedef struct
183 {
184 /* Per-Channel Alerts */
185 /* use a byte, because we need a pointer to each element */
186 guint8 alert_balloon;
187 guint8 alert_beep;
188 guint8 alert_taskbar;
189 guint8 alert_tray;
190
191 /* Per-Channel Settings */
192 guint8 text_hidejoinpart;
193 guint8 text_logging;
194 guint8 text_scrollback;
195 guint8 text_strip;
196
197 char *network;
198 char *channel;
199
200 } chanopt_in_memory;
201
202
203 static chanopt_in_memory *
chanopt_find(char * network,char * channel,gboolean add_new)204 chanopt_find (char *network, char *channel, gboolean add_new)
205 {
206 GSList *list;
207 chanopt_in_memory *co;
208 int i;
209
210 for (list = chanopt_list; list; list = list->next)
211 {
212 co = list->data;
213 if (!g_ascii_strcasecmp (co->channel, channel) &&
214 !g_ascii_strcasecmp (co->network, network))
215 return co;
216 }
217
218 if (!add_new)
219 return NULL;
220
221 /* allocate a new one */
222 co = g_new0 (chanopt_in_memory, 1);
223 co->channel = g_strdup (channel);
224 co->network = g_strdup (network);
225
226 /* set all values to SET_DEFAULT */
227 i = 0;
228 while (i < sizeof (chanopt) / sizeof (channel_options))
229 {
230 *(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = SET_DEFAULT;
231 i++;
232 }
233
234 chanopt_list = g_slist_prepend (chanopt_list, co);
235 chanopt_changed = TRUE;
236
237 return co;
238 }
239
240 static void
chanopt_add_opt(chanopt_in_memory * co,char * var,int new_value)241 chanopt_add_opt (chanopt_in_memory *co, char *var, int new_value)
242 {
243 int i;
244
245 i = 0;
246 while (i < sizeof (chanopt) / sizeof (channel_options))
247 {
248 if (!strcmp (var, chanopt[i].name))
249 {
250 *(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = new_value;
251
252 }
253 i++;
254 }
255 }
256
257 /* load chanopt.conf from disk into our chanopt_list GSList */
258
259 static void
chanopt_load_all(void)260 chanopt_load_all (void)
261 {
262 int fh;
263 char buf[256];
264 char *eq;
265 char *network = NULL;
266 chanopt_in_memory *current = NULL;
267
268 /* 1. load the old file into our GSList */
269 fh = hexchat_open_file ("chanopt.conf", O_RDONLY, 0, 0);
270 if (fh != -1)
271 {
272 while (waitline (fh, buf, sizeof buf, FALSE) != -1)
273 {
274 eq = strchr (buf, '=');
275 if (!eq)
276 continue;
277 eq[0] = 0;
278
279 if (eq != buf && eq[-1] == ' ')
280 eq[-1] = 0;
281
282 if (!strcmp (buf, "network"))
283 {
284 g_free (network);
285 network = g_strdup (eq + 2);
286 }
287 else if (!strcmp (buf, "channel"))
288 {
289 current = chanopt_find (network, eq + 2, TRUE);
290 chanopt_changed = FALSE;
291 }
292 else
293 {
294 if (current)
295 chanopt_add_opt (current, buf, str_to_chanopt (eq + 2));
296 }
297
298 }
299 close (fh);
300 g_free (network);
301 }
302 }
303
304 void
chanopt_load(session * sess)305 chanopt_load (session *sess)
306 {
307 int i;
308 guint8 val;
309 chanopt_in_memory *co;
310 char *network;
311
312 if (sess->session_name[0] == 0)
313 return;
314
315 network = server_get_network (sess->server, FALSE);
316 if (!network)
317 return;
318
319 if (!chanopt_open)
320 {
321 chanopt_open = TRUE;
322 chanopt_load_all ();
323 }
324
325 co = chanopt_find (network, sess->session_name, FALSE);
326 if (!co)
327 return;
328
329 /* fill in all the sess->xxxxx fields */
330 i = 0;
331 while (i < sizeof (chanopt) / sizeof (channel_options))
332 {
333 val = G_STRUCT_MEMBER(guint8, co, chanopt[i].offset);
334 *(guint8 *)G_STRUCT_MEMBER_P(sess, chanopt[i].offset) = val;
335 i++;
336 }
337 }
338
339 void
chanopt_save(session * sess)340 chanopt_save (session *sess)
341 {
342 int i;
343 guint8 vals;
344 guint8 valm;
345 chanopt_in_memory *co;
346 char *network;
347
348 if (sess->session_name[0] == 0)
349 return;
350
351 network = server_get_network (sess->server, FALSE);
352 if (!network)
353 return;
354
355 /* 2. reconcile sess with what we loaded from disk */
356
357 co = chanopt_find (network, sess->session_name, TRUE);
358
359 i = 0;
360 while (i < sizeof (chanopt) / sizeof (channel_options))
361 {
362 vals = G_STRUCT_MEMBER(guint8, sess, chanopt[i].offset);
363 valm = G_STRUCT_MEMBER(guint8, co, chanopt[i].offset);
364
365 if (vals != valm)
366 {
367 *(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = vals;
368 chanopt_changed = TRUE;
369 }
370
371 i++;
372 }
373 }
374
375 static void
chanopt_save_one_channel(chanopt_in_memory * co,int fh)376 chanopt_save_one_channel (chanopt_in_memory *co, int fh)
377 {
378 int i;
379 char buf[256];
380 guint8 val;
381
382 g_snprintf (buf, sizeof (buf), "%s = %s\n", "network", co->network);
383 write (fh, buf, strlen (buf));
384
385 g_snprintf (buf, sizeof (buf), "%s = %s\n", "channel", co->channel);
386 write (fh, buf, strlen (buf));
387
388 i = 0;
389 while (i < sizeof (chanopt) / sizeof (channel_options))
390 {
391 val = G_STRUCT_MEMBER (guint8, co, chanopt[i].offset);
392 if (val != SET_DEFAULT)
393 {
394 g_snprintf (buf, sizeof (buf), "%s = %d\n", chanopt[i].name, val);
395 write (fh, buf, strlen (buf));
396 }
397 i++;
398 }
399 }
400
401 void
chanopt_save_all(gboolean flush)402 chanopt_save_all (gboolean flush)
403 {
404 int i;
405 int num_saved;
406 int fh;
407 GSList *list;
408 chanopt_in_memory *co;
409 guint8 val;
410
411 if (!chanopt_list || !chanopt_changed)
412 {
413 return;
414 }
415
416 fh = hexchat_open_file ("chanopt.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
417 if (fh == -1)
418 {
419 return;
420 }
421
422 for (num_saved = 0, list = chanopt_list; list; list = list->next)
423 {
424 co = list->data;
425
426 i = 0;
427 while (i < sizeof (chanopt) / sizeof (channel_options))
428 {
429 val = G_STRUCT_MEMBER (guint8, co, chanopt[i].offset);
430 /* not using global/default setting, must save */
431 if (val != SET_DEFAULT)
432 {
433 if (num_saved != 0)
434 write (fh, "\n", 1);
435
436 chanopt_save_one_channel (co, fh);
437 num_saved++;
438 goto cont;
439 }
440 i++;
441 }
442
443 cont:
444 if (flush)
445 {
446 g_free (co->network);
447 g_free (co->channel);
448 g_free (co);
449 }
450 }
451
452 close (fh);
453
454 if (flush)
455 {
456 g_slist_free (chanopt_list);
457 chanopt_list = NULL;
458 }
459
460 chanopt_open = FALSE;
461 chanopt_changed = FALSE;
462 }
463