1/******************************************************************************
2
3Copyright 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25Author:  Ralph Mor, X Consortium
26******************************************************************************/
27
28#include "smproxy.h"
29#ifdef HAVE_MKSTEMP
30#include <unistd.h>
31#endif
32
33
34static ProxyFileEntry *proxyFileHead = NULL;
35
36static int write_byte ( FILE *file, unsigned char b );
37static int write_short ( FILE *file, unsigned short s );
38static int write_counted_string ( FILE *file, char *string );
39static int read_byte ( FILE *file, unsigned char *bp );
40static int read_short ( FILE *file, unsigned short *shortp );
41static int read_counted_string ( FILE *file, char **stringp );
42
43#ifndef HAVE_ASPRINTF
44# include <stdarg.h>
45
46/* sprintf variant found in newer libc's which allocates string to print to */
47_X_HIDDEN int _X_ATTRIBUTE_PRINTF(2,3)
48asprintf(char ** ret, const char *format, ...)
49{
50    char buf[256];
51    int len;
52    va_list ap;
53
54    va_start(ap, format);
55    len = vsnprintf(buf, sizeof(buf), format, ap);
56    va_end(ap);
57
58    if (len < 0)
59	return -1;
60
61    if (len < sizeof(buf))
62    {
63	*ret = strdup(buf);
64    }
65    else
66    {
67	*ret = malloc(len + 1); /* snprintf doesn't count trailing '\0' */
68	if (*ret != NULL)
69	{
70	    va_start(ap, format);
71	    len = vsnprintf(*ret, len + 1, format, ap);
72	    va_end(ap);
73	    if (len < 0) {
74		free(*ret);
75		*ret = NULL;
76	    }
77	}
78    }
79
80    if (*ret == NULL)
81	return -1;
82
83    return len;
84}
85#endif
86
87
88
89static int
90write_byte (FILE *file, unsigned char b)
91{
92    if (fwrite (&b, 1, 1, file) != 1)
93	return 0;
94    return 1;
95}
96
97
98static int
99write_short (FILE *file, unsigned short s)
100{
101    unsigned char   file_short[2];
102
103    file_short[0] = (s & (unsigned)0xff00) >> 8;
104    file_short[1] = s & 0xff;
105    if (fwrite (file_short, sizeof (file_short), 1, file) != 1)
106	return 0;
107    return 1;
108}
109
110
111static int
112write_counted_string(FILE *file, char *string)
113{
114    if (string)
115    {
116	size_t count = strlen (string);
117
118	if (write_byte (file, (unsigned char)count) == 0)
119	    return 0;
120	if (fwrite (string, sizeof (char), count, file) != count)
121	    return 0;
122    }
123    else
124    {
125	if (write_byte (file, 0) == 0)
126	    return 0;
127    }
128
129    return 1;
130}
131
132
133
134static int
135read_byte(FILE *file, unsigned char *bp)
136{
137    if (fread (bp, 1, 1, file) != 1)
138	return 0;
139    return 1;
140}
141
142
143static int
144read_short(FILE *file, unsigned short *shortp)
145{
146    unsigned char   file_short[2];
147
148    if (fread (file_short, sizeof (file_short), 1, file) != 1)
149	return 0;
150    *shortp = file_short[0] * 256 + file_short[1];
151    return 1;
152}
153
154
155static int
156read_counted_string(FILE *file, char **stringp)
157{
158    unsigned char  len;
159    char	   *data;
160
161    if (read_byte (file, &len) == 0)
162	return 0;
163    if (len == 0) {
164	data = NULL;
165    } else {
166	data = malloc ((size_t) len + 1);
167    	if (!data)
168	    return 0;
169	if (fread (data, sizeof (char), (size_t) len, file) != len) {
170	    free (data);
171	    return 0;
172    	}
173	data[len] = '\0';
174    }
175    *stringp = data;
176    return 1;
177}
178
179
180
181/*
182 * An entry in the .smproxy file looks like this:
183 *
184 * FIELD				BYTES
185 * -----                                ----
186 * client ID len			1
187 * client ID				LIST of bytes
188 * WM_CLASS "res name" length		1
189 * WM_CLASS "res name"			LIST of bytes
190 * WM_CLASS "res class" length          1
191 * WM_CLASS "res class"                 LIST of bytes
192 * WM_NAME length			1
193 * WM_NAME				LIST of bytes
194 * WM_COMMAND arg count			1
195 * For each arg in WM_COMMAND
196 *    arg length			1
197 *    arg				LIST of bytes
198 */
199
200int
201WriteProxyFileEntry(FILE *proxyFile, WinInfo *theWindow)
202{
203    if (!write_counted_string (proxyFile, theWindow->client_id))
204	return 0;
205    if (!write_counted_string (proxyFile, theWindow->class.res_name))
206	return 0;
207    if (!write_counted_string (proxyFile, theWindow->class.res_class))
208	return 0;
209    if (!write_counted_string (proxyFile, theWindow->wm_name))
210	return 0;
211
212    if (!theWindow->wm_command || theWindow->wm_command_count == 0)
213    {
214	if (!write_byte (proxyFile, 0))
215	    return 0;
216    }
217    else
218    {
219	if (!write_byte (proxyFile, (char) theWindow->wm_command_count))
220	    return 0;
221	for (int i = 0; i < theWindow->wm_command_count; i++)
222	    if (!write_counted_string (proxyFile, theWindow->wm_command[i]))
223		return 0;
224    }
225
226    return 1;
227}
228
229
230int
231ReadProxyFileEntry(FILE *proxyFile, ProxyFileEntry **pentry)
232{
233    ProxyFileEntry *entry;
234    unsigned char byte;
235    int i;
236
237    *pentry = entry = malloc (sizeof (ProxyFileEntry));
238    if (!*pentry)
239	return 0;
240
241    entry->tag = 0;
242    entry->client_id = NULL;
243    entry->class.res_name = NULL;
244    entry->class.res_class = NULL;
245    entry->wm_name = NULL;
246    entry->wm_command = NULL;
247    entry->wm_command_count = 0;
248
249    if (!read_counted_string (proxyFile, &entry->client_id))
250	goto give_up;
251    if (!read_counted_string (proxyFile, &entry->class.res_name))
252	goto give_up;
253    if (!read_counted_string (proxyFile, &entry->class.res_class))
254	goto give_up;
255    if (!read_counted_string (proxyFile, &entry->wm_name))
256	goto give_up;
257
258    if (!read_byte (proxyFile, &byte))
259	goto give_up;
260    entry->wm_command_count = byte;
261
262    if (entry->wm_command_count == 0)
263	entry->wm_command = NULL;
264    else
265    {
266	entry->wm_command = calloc (entry->wm_command_count, sizeof (char *));
267
268	if (!entry->wm_command)
269	    goto give_up;
270
271	for (i = 0; i < entry->wm_command_count; i++)
272	    if (!read_counted_string (proxyFile, &entry->wm_command[i]))
273		goto give_up;
274    }
275
276    return 1;
277
278give_up:
279
280    if (entry->client_id)
281	free (entry->client_id);
282    if (entry->class.res_name)
283	free (entry->class.res_name);
284    if (entry->class.res_class)
285	free (entry->class.res_class);
286    if (entry->wm_name)
287	free (entry->wm_name);
288    if (entry->wm_command)
289    {
290        if (entry->wm_command_count)
291        {
292	    for (i = 0; i < entry->wm_command_count; i++)
293	        if (entry->wm_command[i])
294		    free (entry->wm_command[i]);
295        }
296	free (entry->wm_command);
297    }
298
299    free (entry);
300    *pentry = NULL;
301
302    return 0;
303}
304
305
306void
307ReadProxyFile(char *filename)
308{
309    FILE *proxyFile;
310    ProxyFileEntry *entry;
311    int done = 0;
312    unsigned short version;
313
314    proxyFile = fopen (filename, "rb");
315    if (!proxyFile)
316	return;
317
318    if (!read_short (proxyFile, &version) ||
319	version > SAVEFILE_VERSION)
320    {
321	done = 1;
322    }
323
324    while (!done)
325    {
326	if (ReadProxyFileEntry (proxyFile, &entry))
327	{
328	    entry->next = proxyFileHead;
329	    proxyFileHead = entry;
330	}
331	else
332	    done = 1;
333    }
334
335    fclose (proxyFile);
336}
337
338
339
340static char *
341unique_filename(const char *path, const char *prefix, int *pFd)
342{
343    char *tempFile = NULL;
344    int tempFd = 0;
345
346#if defined(HAVE_MKSTEMP) || defined(HAVE_MKTEMP)
347    if (asprintf (&tempFile, "%s/%sXXXXXX", path, prefix) == -1)
348	return NULL;
349#endif
350
351#ifdef HAVE_MKSTEMP
352    tempFd = mkstemp(tempFile);
353#else
354
355# ifdef HAVE_MKTEMP
356    if (mktemp(tempFile) == NULL)
357	tempFd = -1;
358# else /* fallback to tempnam */
359    tempFile = tempnam (path, prefix);
360# endif /* HAVE_MKTEMP */
361
362    if (tempFd != -1 && tempFile != NULL)
363	tempFd = open(tempFile, O_RDWR | O_CREAT | O_EXCL, 0600);
364#endif
365
366    if (tempFd == -1) {
367	free(tempFile);
368	return (NULL);
369    }
370
371    *pFd = tempFd;
372    return tempFile;
373
374}
375
376
377
378char *
379WriteProxyFile(void)
380{
381    FILE *proxyFile = NULL;
382    char *filename = NULL;
383    int fd = -1;
384    const char *path;
385    WinInfo *winptr;
386    Bool success = False;
387
388    path = getenv ("SM_SAVE_DIR");
389    if (!path)
390    {
391	path = getenv ("HOME");
392	if (!path)
393	    path = ".";
394    }
395
396    if ((filename = unique_filename (path, ".prx", &fd)) == NULL)
397	goto bad;
398
399    if (!(proxyFile = fdopen(fd, "wb")))
400	goto bad;
401
402    if (!write_short (proxyFile, SAVEFILE_VERSION))
403	goto bad;
404
405    success = True;
406    winptr = win_head;
407
408    while (winptr && success)
409    {
410	if (winptr->client_id)
411	    if (!WriteProxyFileEntry (proxyFile, winptr))
412	    {
413		success = False;
414		break;
415	    }
416
417	winptr = winptr->next;
418    }
419
420 bad:
421
422    if (proxyFile)
423	fclose (proxyFile);
424    else if (fd != -1)
425	close (fd);
426
427    if (success)
428	return (filename);
429    else
430    {
431	if (filename)
432	    free (filename);
433	return (NULL);
434    }
435}
436
437
438
439char *
440LookupClientID(WinInfo *theWindow)
441{
442    ProxyFileEntry *ptr;
443    int found = 0;
444
445    ptr = proxyFileHead;
446    while (ptr && !found)
447    {
448	if (!ptr->tag &&
449            strcmp (theWindow->class.res_name, ptr->class.res_name) == 0 &&
450	    strcmp (theWindow->class.res_class, ptr->class.res_class) == 0 &&
451	    strcmp (theWindow->wm_name, ptr->wm_name) == 0)
452	{
453	    if (theWindow->wm_command_count == ptr->wm_command_count)
454	    {
455		int i;
456
457		for (i = 0; i < theWindow->wm_command_count; i++)
458		    if (strcmp (theWindow->wm_command[i],
459			ptr->wm_command[i]) != 0)
460			break;
461
462		if (i == theWindow->wm_command_count)
463		    found = 1;
464	    }
465	}
466
467	if (!found)
468	    ptr = ptr->next;
469    }
470
471    if (found)
472    {
473	ptr->tag = 1;
474	return (ptr->client_id);
475    }
476    else
477	return NULL;
478}
479