source: trunk/gtk/conf.c @ 1998

Last change on this file since 1998 was 1998, checked in by livings124, 15 years ago

patches from Charles Kerr to update the Inspector in GTK and fix some memory leaks

  • Property svn:keywords set to Date Rev Author Id
File size: 13.5 KB
Line 
1/******************************************************************************
2 * $Id: conf.c 1998 2007-06-06 00:30:13Z livings124 $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <ctype.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <pwd.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <sys/stat.h>
34#include <unistd.h>
35
36#include <glib.h>
37#include <glib/gi18n.h>
38
39#include "conf.h"
40#include "util.h"
41
42#define CONF_SUBDIR             "gtk"
43#define FILE_LOCK               "lock"
44#define FILE_SOCKET             "socket"
45#define FILE_PREFS              "prefs"
46#define FILE_PREFS_TMP          "prefs.tmp"
47#define FILE_STATE              "state"
48#define FILE_STATE_TMP          "state.tmp"
49#define OLD_FILE_LOCK           "gtk_lock" /* remove this after next release */
50#define OLD_FILE_PREFS          "gtk_prefs"
51#define OLD_FILE_STATE          "gtk_state"
52#define PREF_SEP_KEYVAL         '\t'
53#define PREF_SEP_LINE           '\n'
54#define STATE_SEP               '\n'
55
56static int
57lockfile(const char *file, char **errstr);
58static void
59cf_removelocks(void);
60static char *
61cf_readfile(const char *file, const char *oldfile, gsize *len,
62            gboolean *usedold, char **errstr);
63static void
64cf_benc_append(benc_val_t *val, char type, int incsize);
65static void
66cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
67             char **errstr);
68static gboolean
69writefile_traverse(gpointer key, gpointer value, gpointer data);
70static char *
71getstateval(benc_val_t *state, char *line);
72
73static char *gl_confdir = NULL;
74static char *gl_old_confdir = NULL;
75static GTree *gl_prefs = NULL;
76static char *gl_lockpath = NULL;
77static char *gl_old_lockpath = NULL;
78
79/* errstr may be NULL, this might be called before GTK is initialized */
80static int
81lockfile(const char *file, char **errstr) {
82  int fd, savederr;
83  struct flock lk;
84
85  if(NULL != errstr)
86    *errstr = NULL;
87
88  if(0 > (fd = open(file, O_RDWR | O_CREAT, 0666))) {
89    savederr = errno;
90    if(NULL != errstr)
91      *errstr = g_strdup_printf(_("Failed to open the file %s for writing:\n%s"),
92                                file, strerror(errno));
93    errno = savederr;
94    return -1;
95  }
96
97  bzero(&lk, sizeof(lk));
98  lk.l_start = 0;
99  lk.l_len = 0;
100  lk.l_type = F_WRLCK;
101  lk.l_whence = SEEK_SET;
102  if(-1 == fcntl(fd, F_SETLK, &lk)) {
103    savederr = errno;
104    if(NULL != errstr) {
105      if(EAGAIN == errno)
106        *errstr = g_strdup_printf(_("Another copy of %s is already running."),
107                                  g_get_application_name());
108      else
109        *errstr = g_strdup_printf(_("Failed to lock the file %s:\n%s"),
110                                  file, strerror(errno));
111    }
112    close(fd);
113    errno = savederr;
114    return -1;
115  }
116
117  return fd;
118}
119
120/* errstr may be NULL, this might be called before GTK is initialized */
121gboolean
122cf_init(const char *dir, char **errstr) {
123  if(NULL != errstr)
124    *errstr = NULL;
125  gl_old_confdir = g_strdup(dir);
126  gl_confdir = g_build_filename(dir, CONF_SUBDIR, NULL);
127
128  if(mkdir_p(gl_confdir, 0777))
129    return TRUE;
130
131  if(NULL != errstr)
132    *errstr = g_strdup_printf(_("Failed to create the directory %s:\n%s"),
133                              gl_confdir, strerror(errno));
134  return FALSE;
135}
136
137/* errstr may be NULL, this might be called before GTK is initialized */
138gboolean
139cf_lock(char **errstr) {
140  char *path = g_build_filename(gl_old_confdir, OLD_FILE_LOCK, NULL);
141  int fd = lockfile(path, errstr);
142
143  if(0 > fd)
144    g_free(path);
145  else {
146    gl_old_lockpath = path;
147    path = g_build_filename(gl_confdir, FILE_LOCK, NULL);
148    fd = lockfile(path, errstr);
149    if(0 > fd)
150      g_free(path);
151    else
152      gl_lockpath = path;
153  }
154
155  g_atexit(cf_removelocks);
156
157  return 0 <= fd;
158}
159
160static void
161cf_removelocks(void) {
162  if(NULL != gl_lockpath) {
163    unlink(gl_lockpath);
164    g_free(gl_lockpath);
165  }
166
167  if(NULL != gl_old_lockpath) {
168    unlink(gl_old_lockpath);
169    g_free(gl_old_lockpath);
170  }
171}
172
173char *
174cf_sockname(void) {
175  return g_build_filename(gl_confdir, FILE_SOCKET, NULL);
176}
177
178static char *
179cf_readfile(const char *file, const char *oldfile, gsize *len,
180            gboolean *usedold, char **errstr) {
181  char *path;
182  GIOChannel *io;
183  GError *err;
184  char *ret;
185
186  *errstr = NULL;
187  *usedold = FALSE;
188  ret = NULL;
189  err = NULL;
190  *len = 0;
191
192  path = g_build_filename(gl_confdir, file, NULL);
193  io = g_io_channel_new_file(path, "r", &err);
194  if(NULL != err && g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
195    g_error_free(err);
196    err = NULL;
197    g_free(path);
198    path = g_build_filename(gl_old_confdir, oldfile, NULL);
199    io = g_io_channel_new_file(path, "r", &err);
200    *usedold = TRUE;
201  }
202  if(NULL != err) {
203    if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
204      *errstr = g_strdup_printf(
205        _("Failed to open the file %s for reading:\n%s"), path, err->message);
206    goto done;
207  }
208  g_io_channel_set_encoding(io, NULL, NULL);
209
210  if(G_IO_STATUS_ERROR == g_io_channel_read_to_end(io, &ret, len, &err)) {
211    *errstr = g_strdup_printf(
212      _("Error while reading from the file %s:\n%s"), path, err->message);
213    goto done;
214  }
215
216 done:
217  g_free (path);
218  if(NULL != err)
219    g_error_free(err);
220  if(NULL != io) 
221    g_io_channel_unref(io);
222  return ret;
223}
224
225void
226cf_loadprefs(char **errstr) {
227  char *data, *line, *eol, *sep, *key;
228  gsize len;
229  benc_val_t val;
230  gboolean usedold;
231  int ii;
232
233  *errstr = NULL;
234
235  if(NULL != gl_prefs)
236    g_tree_destroy(gl_prefs);
237
238  gl_prefs = g_tree_new_full((GCompareDataFunc)g_ascii_strcasecmp, NULL,
239                          g_free, g_free);
240
241  data = cf_readfile(FILE_PREFS, OLD_FILE_PREFS, &len, &usedold, errstr);
242  if(NULL != *errstr) {
243    g_assert(NULL == data);
244    return;
245  }
246
247  if(NULL == data)
248    return;
249
250  bzero(&val, sizeof(val));
251  if(!usedold && !tr_bencLoad(data, len, &val, NULL)) {
252    if(TYPE_DICT == val.type) {
253      key = NULL;
254      for(ii = 0; ii < val.val.l.count; ii++) {
255        if(NULL == key) {
256          g_assert(TYPE_STR == val.val.l.vals[ii].type);
257          key = val.val.l.vals[ii].val.s.s;
258        } else {
259          if(TYPE_INT == val.val.l.vals[ii].type)
260            g_tree_insert(gl_prefs, g_strdup(key),
261                         g_strdup_printf("%"PRIu64, val.val.l.vals[ii].val.i));
262          else if(TYPE_STR == val.val.l.vals[ii].type)
263            g_tree_insert(gl_prefs, g_strdup(key),
264                          g_strdup(val.val.l.vals[ii].val.s.s));
265          key = NULL;
266        }
267      }
268    }
269
270  } else {
271    /* XXX remove this in a release or two */
272    for(line = data; NULL != (eol = strchr(line, PREF_SEP_LINE));
273        line = eol + 1) {
274      *eol = '\0';
275      if(g_utf8_validate(line, -1, NULL) &&
276         NULL != (sep = strchr(line, PREF_SEP_KEYVAL))) {
277        *sep = '\0';
278        g_tree_insert(gl_prefs, g_strcompress(line), g_strcompress(sep+1));
279      }
280    }
281    cf_saveprefs(errstr);
282  }
283
284  tr_bencFree(&val);
285  g_free(data);
286}
287
288benc_val_t *
289cf_loadstate(char **errstr) {
290  char *data, *line, *eol, *prog;
291  gsize len;
292  gboolean usedold;
293  benc_val_t *state, *torstate;
294
295  *errstr = NULL;
296
297  data = cf_readfile(FILE_STATE, OLD_FILE_STATE, &len, &usedold, errstr);
298  if(NULL != *errstr) {
299    g_assert(NULL == data);
300    return NULL;
301  }
302
303  if(NULL == data)
304    return NULL;
305
306  state = g_new0(benc_val_t, 1);
307  if(usedold || tr_bencLoad(data, len, state, NULL)) {
308    /* XXX all this evil compat code should go away at some point */
309    tr_bencFree(state);
310    bzero(state, sizeof(benc_val_t));
311    state->type = TYPE_LIST;
312    for(line = data; NULL != (eol = strchr(line, PREF_SEP_LINE));
313        line = eol + 1) {
314      *eol = '\0';
315      if(g_utf8_validate(line, -1, NULL)) {
316        cf_benc_append(state, TYPE_DICT, 10);
317        torstate = state->val.l.vals + state->val.l.count - 1;
318        prog = line;
319        while(NULL != (prog = getstateval(torstate, prog)))
320          ;
321      }
322    }
323  }
324
325  g_free(data);
326
327  return state;
328}
329
330static void
331cf_benc_append(benc_val_t *val, char type, int incsize) {
332  if(++val->val.l.count > val->val.l.alloc) {
333    val->val.l.alloc += incsize;
334    val->val.l.vals = g_renew(benc_val_t, val->val.l.vals, val->val.l.alloc);
335    bzero(val->val.l.vals + val->val.l.alloc - incsize,
336          incsize * sizeof(benc_val_t));
337  }
338  val->val.l.vals[val->val.l.count-1].type = type;
339}
340
341const char *
342cf_getpref(const char *name) {
343  g_assert(NULL != gl_prefs);
344
345  return g_tree_lookup(gl_prefs, name);
346}
347
348void
349cf_setpref(const char *name, const char *value) {
350  g_assert(NULL != gl_prefs);
351
352  g_tree_insert(gl_prefs, g_strdup(name), g_strdup(value));
353}
354
355static void
356cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
357             char **errstr) {
358  char *path = g_build_filename(gl_confdir, file, NULL);
359  char *pathtmp = g_build_filename(gl_confdir, tmp, NULL);
360  GIOChannel *io = NULL;
361  GError *err;
362  char *datastr;
363  int len;
364  gsize written;
365
366  *errstr = NULL;
367  err = NULL;
368  datastr = NULL;
369
370  io = g_io_channel_new_file(pathtmp, "w", &err);
371  if(NULL != err) {
372    *errstr = g_strdup_printf(_("Failed to open the file %s for writing:\n%s"),
373                              pathtmp, err->message);
374    goto done;
375  }
376  g_io_channel_set_encoding(io, NULL, NULL);
377
378  len = 0;
379  datastr = tr_bencSaveMalloc(data, &len);
380
381  written = 0;
382  g_io_channel_write_chars(io, datastr, len, &written, &err);
383  if(NULL != err)
384    g_io_channel_flush(io, &err);
385  if(NULL != err) {
386    *errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
387                              pathtmp, err->message);
388    goto done;
389  }
390
391  if(0 > rename(pathtmp, path)) {
392    *errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
393                              pathtmp, file, strerror(errno));
394    goto done;
395  }
396
397 done:
398  g_free(path);
399  g_free(pathtmp);
400  if(NULL != io)
401    g_io_channel_unref(io);
402  if(NULL != datastr)
403    free(datastr);
404}
405
406void
407cf_saveprefs(char **errstr) {
408  benc_val_t val;
409  benc_val_t *ptr;
410
411  *errstr = NULL;
412
413  bzero(&val, sizeof(val));
414  val.type = TYPE_DICT;
415  val.val.l.alloc = val.val.l.count = g_tree_nnodes(gl_prefs) * 2;
416  val.val.l.vals = g_new0(benc_val_t, val.val.l.alloc);
417
418  ptr = val.val.l.vals;
419  g_tree_foreach(gl_prefs, writefile_traverse, &ptr);
420  g_assert(ptr - val.val.l.vals == val.val.l.alloc);
421
422  cf_writebenc(FILE_PREFS, FILE_PREFS_TMP, &val, errstr);
423  tr_bencFree(&val);
424}
425
426static gboolean
427writefile_traverse(gpointer key, gpointer value, gpointer data) {
428  benc_val_t **ptr = data;
429  benc_val_t *bkey = *ptr;
430  benc_val_t *bval = (*ptr) + 1;
431
432  *ptr = (*ptr) + 2;
433
434  bkey->type = TYPE_STR;
435  bkey->val.s.s = g_strdup(key);
436  bkey->val.s.i = strlen(key);
437
438  bval->type = TYPE_STR;
439  bval->val.s.s = g_strdup(value);
440  bval->val.s.i = strlen(value);
441
442  return FALSE;
443}
444
445static char *
446getstateval(benc_val_t *state, char *line) {
447  char *start, *end;
448
449  /* skip any leading whitespace */
450  while(g_ascii_isspace(*line))
451    line++;
452
453  /* walk over the key, which may be alphanumerics as well as - or _ */
454  for(start = line; g_ascii_isalnum(*start)
455        || '_' == *start || '-' == *start; start++)
456    ;
457
458  /* they key must be immediately followed by an = */
459  if('=' != *start)
460    return NULL;
461  *(start++) = '\0';
462
463  /* then the opening quote for the value */
464  if('"' != *(start++))
465    return NULL;
466
467  /* walk over the value */
468  for(end = start; '\0' != *end && '"' != *end; end++)
469    /* skip over escaped quotes */
470    if('\\' == *end && '\0' != *(end + 1))
471      end++;
472
473  /* make sure we didn't hit the end of the string */
474  if('"' != *end)
475    return NULL;
476  *end = '\0';
477
478  /* if it's a key we recognize then save the data */
479  if(0 == strcmp(line, "torrent") || 0 == strcmp(line, "dir") ||
480     0 == strcmp(line, "paused")) {
481    cf_benc_append(state, TYPE_STR, 6);
482    state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(line);
483    state->val.l.vals[state->val.l.count-1].val.s.i = strlen(line);
484    if('p' == *line) {
485      cf_benc_append(state, TYPE_INT, 6);
486      state->val.l.vals[state->val.l.count-1].val.i = strbool(start);
487    } else {
488      cf_benc_append(state, TYPE_STR, 6);
489      state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(start);
490      state->val.l.vals[state->val.l.count-1].val.s.i = strlen(start);
491    }
492  }
493
494  /* return a pointer to just past the end of the value */
495  return end + 1;
496}
497
498void
499cf_savestate(benc_val_t *state, char **errstr) {
500  *errstr = NULL;
501  cf_writebenc(FILE_STATE, FILE_STATE_TMP, state, errstr);
502}
503
504void
505cf_freestate( benc_val_t * state )
506{
507    if( NULL != state )
508    {
509        tr_bencFree( state );
510        g_free( state );
511    }
512}
Note: See TracBrowser for help on using the repository browser.