1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2021 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "config.h"
25 
26 #include <X11/Xlib.h>
27 
28 #include "E.h"
29 #include "comms.h"
30 #include "hints.h"
31 #include "ipc.h"
32 #include "list.h"
33 #include "xprop.h"
34 #include "xwin.h"
35 
36 typedef struct {
37    dlist_t             list;
38    char               *name;
39    EX_Window           xwin;
40    char               *msg;
41    char               *clientname;
42    char               *version;
43    char               *info;
44    char                replied;
45 } Client;
46 
47 static void         CommsSend(Client * c, const char *s);
48 
49 static              LIST_HEAD(client_list);
50 
51 static Win          comms_win = NULL;
52 
53 static Client      *
ClientCreate(EX_Window xwin)54 ClientCreate(EX_Window xwin)
55 {
56    Client             *c;
57    char                st[32];
58 
59    c = ECALLOC(Client, 1);
60    if (!c)
61       return NULL;
62 
63    Esnprintf(st, sizeof(st), "%8x", (int)xwin);
64    c->name = Estrdup(st);
65    c->xwin = xwin;
66 
67    LIST_PREPEND(Client, &client_list, c);
68 
69    return c;
70 }
71 
72 static void
ClientDestroy(Client * c)73 ClientDestroy(Client * c)
74 {
75    if (!c)
76       return;
77 
78    LIST_REMOVE(Client, &client_list, c);
79 
80    Efree(c->name);
81    Efree(c->msg);
82    Efree(c->clientname);
83    Efree(c->version);
84    Efree(c->info);
85 
86    Efree(c);
87 }
88 
89 static int
ClientConfigure(Client * c,const char * str)90 ClientConfigure(Client * c, const char *str)
91 {
92    char                param[64];
93    const char         *value;
94    int                 len;
95 
96    len = 0;
97    sscanf(str, "%*s %60s %n", param, &len);
98    value = str + len;
99 
100    if (!strcmp(param, "clientname"))
101      {
102 	EFREE_DUP(c->clientname, value);
103      }
104    else if (!strcmp(param, "version"))
105      {
106 	EFREE_DUP(c->version, value);
107      }
108    else if (!strcmp(param, "author"))
109      {
110      }
111    else if (!strcmp(param, "email"))
112      {
113      }
114    else if (!strcmp(param, "web"))
115      {
116      }
117    else if (!strcmp(param, "address"))
118      {
119      }
120    else if (!strcmp(param, "info"))
121      {
122 	EFREE_DUP(c->info, value);
123      }
124    else if (!strcmp(param, "pixmap"))
125      {
126      }
127    else
128      {
129 	return -1;
130      }
131 
132    return 0;
133 }
134 
135 static int
ClientMatchWindow(const void * data,const void * match)136 ClientMatchWindow(const void *data, const void *match)
137 {
138    return ((const Client *)data)->xwin != (EX_Window) (long)match;
139 }
140 
141 static Client      *
ClientFind(EX_Window xwin)142 ClientFind(EX_Window xwin)
143 {
144    return LIST_FIND(Client, &client_list, ClientMatchWindow,
145 		    (void *)(long)xwin);
146 }
147 
148 static char        *
ClientCommsGet(Client ** c,XClientMessageEvent * ev)149 ClientCommsGet(Client ** c, XClientMessageEvent * ev)
150 {
151    char                s[13], s2[9], *msg;
152    unsigned int        i;
153    EX_Window           xwin;
154    Client             *cl;
155 
156    if ((!ev) || (!c))
157       return NULL;
158    if (ev->message_type != ea_m.ENL_MSG)
159       return NULL;
160 
161    s[12] = 0;
162    s2[8] = 0;
163    for (i = 0; i < 8; i++)
164       s2[i] = ev->data.b[i];
165    for (i = 0; i < 12; i++)
166       s[i] = ev->data.b[i + 8];
167    xwin = NoXID;
168    sscanf(s2, "%x", &xwin);
169    if (xwin == NoXID)
170       return NULL;
171    cl = ClientFind(xwin);
172    if (!cl)
173      {
174 	cl = ClientCreate(xwin);
175 	if (!cl)
176 	   return NULL;
177      }
178 
179    /* append text to end of msg */
180    i = (cl->msg) ? strlen(cl->msg) : 0;
181    cl->msg = EREALLOC(char, cl->msg, i + strlen(s) + 1);
182    if (!cl->msg)
183       return NULL;
184    strcpy(cl->msg + i, s);
185 
186    msg = NULL;
187    if (strlen(s) < 12)
188      {
189 	msg = cl->msg;
190 	cl->msg = NULL;
191 	*c = cl;
192      }
193 
194    return msg;
195 }
196 
197 static void
ClientIpcReply(void * data,const char * str)198 ClientIpcReply(void *data, const char *str)
199 {
200    Client             *c = (Client *) data;
201 
202    if (!c)
203       return;
204 
205    if (!str)
206      {
207 	/* Don't send empty replies (ack's) if we ever have replied to this
208 	 * client. Without this hack communication with e.g. epplets fails. */
209 	if (c->replied)
210 	   return;
211 	str = "";
212      }
213    CommsSend(c, str);
214    c->replied = 1;
215 }
216 
217 static void
ClientHandleComms(XClientMessageEvent * ev)218 ClientHandleComms(XClientMessageEvent * ev)
219 {
220    Client             *c;
221    char               *s;
222 
223    s = ClientCommsGet(&c, ev);
224    if (!s)
225       return;
226 
227    if (EDebug(EDBUG_TYPE_IPC))
228       Eprintf("%s: %s\n", __func__, s);
229 
230    if (!strncmp(s, "set ", 4))
231      {
232 	/* The old Client set command (used by epplets) */
233 	if (ClientConfigure(c, s) == 0)
234 	   goto done;
235      }
236 
237    if (!IpcExecReply(s, ClientIpcReply, c))
238      {
239 #if ENABLE_DIALOGS
240 	const char         *s1, *s2;
241 
242 	s1 = (c->clientname) ? c->clientname : "UNKNOWN";
243 	s2 = (c->version) ? c->version : "UNKNOWN";
244 	DialogOK(_("E IPC Error"),
245 		 _("Received Unknown Client Message.\n"
246 		   "Client Name:    %s\n" "Client Version: %s\n"
247 		   "Message Contents:\n\n" "%s\n"), s1, s2, s);
248 #endif
249 	SoundPlay(SOUND_ERROR_IPC);
250      }
251 
252  done:
253    Efree(s);
254 }
255 
256 static void
ClientHandleRootEvents(Win win __UNUSED__,XEvent * ev,void * prm __UNUSED__)257 ClientHandleRootEvents(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
258 {
259    Client             *c;
260 
261 #if 0
262    Eprintf("%s: type=%d win=%#lx\n", __func__, ev->type, ev->xany.window);
263 #endif
264    switch (ev->type)
265      {
266      case DestroyNotify:
267 	c = ClientFind(ev->xdestroywindow.window);
268 	if (!c)
269 	   break;
270 	ClientDestroy(c);
271 	break;
272      }
273 }
274 
275 static void
ClientHandleCommsEvents(Win win __UNUSED__,XEvent * ev,void * prm __UNUSED__)276 ClientHandleCommsEvents(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
277 {
278 #if 0
279    Eprintf("%s: type=%d win=%#lx\n", __func__, ev->type, ev->xany.window);
280 #endif
281    switch (ev->type)
282      {
283      case ClientMessage:
284 	ClientHandleComms(&(ev->xclient));
285 	break;
286      }
287 }
288 
289 void
CommsInit(void)290 CommsInit(void)
291 {
292    char                s[1024];
293 
294    comms_win = ECreateEventWindow(VROOT, -100, -100, 5, 5);
295    ESelectInput(comms_win, StructureNotifyMask | SubstructureNotifyMask);
296    EventCallbackRegister(comms_win, ClientHandleCommsEvents, NULL);
297    EventCallbackRegister(VROOT, ClientHandleRootEvents, NULL);
298 
299    Esnprintf(s, sizeof(s), "WINID %8x", WinGetXwin(comms_win));
300    ex_window_prop_string_set(WinGetXwin(comms_win), ea_m.ENLIGHTENMENT_COMMS,
301 			     s);
302    ex_window_prop_string_set(WinGetXwin(VROOT), ea_m.ENLIGHTENMENT_COMMS, s);
303 }
304 
305 static void
CommsDoSend(EX_Window win,const char * s)306 CommsDoSend(EX_Window win, const char *s)
307 {
308    char                ss[21];
309    int                 i, j, k, len;
310    XEvent              ev;
311 
312    if ((!win) || (!s))
313       return;
314 
315    len = strlen(s);
316    ev.xclient.type = ClientMessage;
317    ev.xclient.serial = 0;
318    ev.xclient.send_event = True;
319    ev.xclient.window = win;
320    ev.xclient.message_type = ea_m.ENL_MSG;
321    ev.xclient.format = 8;
322    for (i = 0; i < len + 1; i += 12)
323      {
324 	Esnprintf(ss, sizeof(ss), "%8x", WinGetXwin(comms_win));
325 	for (j = 0; j < 12; j++)
326 	  {
327 	     ss[8 + j] = s[i + j];
328 	     if (!s[i + j])
329 		break;
330 	  }
331 	ss[20] = 0;
332 	for (k = 0; k < 20; k++)
333 	   ev.xclient.data.b[k] = ss[k];
334 	EXSendEvent(win, 0, (XEvent *) & ev);
335      }
336 }
337 
338 static void
CommsSend(Client * c,const char * s)339 CommsSend(Client * c, const char *s)
340 {
341    if (!c)
342       return;
343 
344    CommsDoSend(c->xwin, s);
345 }
346