source: trunk/gtk/conf.c @ 3209

Last change on this file since 3209 was 3209, checked in by charles, 15 years ago

now that the gtk+ prefs are unfucked, add an "ignore unencrypted peers" preference.

  • Property svn:keywords set to Date Rev Author Id
File size: 13.5 KB
Line 
1/******************************************************************************
2 * $Id: conf.c 3209 2007-09-28 00:46:22Z charles $
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#include <glib/gstdio.h>
39
40#include <libtransmission/transmission.h>
41#include <libtransmission/bencode.h>
42
43#include "conf.h"
44#include "util.h"
45
46#define CONF_SUBDIR             "gtk"
47#define FILE_LOCK               "lock"
48#define FILE_SOCKET             "socket"
49#define FILE_PREFS              "prefs"
50#define FILE_PREFS_TMP          "prefs.tmp"
51#define FILE_STATE              "state"
52#define FILE_STATE_TMP          "state.tmp"
53#define OLD_FILE_LOCK           "gtk_lock" /* remove this after next release */
54#define OLD_FILE_PREFS          "gtk_prefs"
55#define OLD_FILE_STATE          "gtk_state"
56#define PREF_SEP_KEYVAL         '\t'
57#define PREF_SEP_LINE           '\n'
58#define STATE_SEP               '\n'
59
60static int
61lockfile(const char *file, char **errstr);
62static void
63cf_removelocks(void);
64static char *
65cf_readfile(const char *file, const char *oldfile, gsize *len,
66            gboolean *usedold, char **errstr);
67static void
68cf_benc_append(benc_val_t *val, char type, int incsize);
69static void
70cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
71             char **errstr);
72static char *
73getstateval(benc_val_t *state, char *line);
74
75static char *gl_confdir = NULL;
76static char *gl_old_confdir = NULL;
77static char *gl_lockpath = NULL;
78static char *gl_old_lockpath = NULL;
79
80/* errstr may be NULL, this might be called before GTK is initialized */
81static int
82lockfile(const char *file, char **errstr) {
83  int fd, savederr;
84  struct flock lk;
85
86  if(NULL != errstr)
87    *errstr = NULL;
88
89  if(0 > (fd = open(file, O_RDWR | O_CREAT, 0666))) {
90    savederr = errno;
91    if(NULL != errstr)
92      *errstr = g_strdup_printf(_("Failed to open the file %s for writing:\n%s"),
93                                file, strerror(errno));
94    errno = savederr;
95    return -1;
96  }
97
98  memset(&lk, 0,  sizeof(lk));
99  lk.l_start = 0;
100  lk.l_len = 0;
101  lk.l_type = F_WRLCK;
102  lk.l_whence = SEEK_SET;
103  if(-1 == fcntl(fd, F_SETLK, &lk)) {
104    savederr = errno;
105    if(NULL != errstr) {
106      if(EAGAIN == errno)
107        *errstr = g_strdup_printf(_("Another copy of %s is already running."),
108                                  g_get_application_name());
109      else
110        *errstr = g_strdup_printf(_("Failed to lock the file %s:\n%s"),
111                                  file, strerror(errno));
112    }
113    close(fd);
114    errno = savederr;
115    return -1;
116  }
117
118  return fd;
119}
120
121/* errstr may be NULL, this might be called before GTK is initialized */
122gboolean
123cf_init(const char *dir, char **errstr) {
124  if(NULL != errstr)
125    *errstr = NULL;
126  gl_old_confdir = g_strdup(dir);
127  gl_confdir = g_build_filename(dir, CONF_SUBDIR, NULL);
128
129  if(mkdir_p(gl_confdir, 0777))
130    return TRUE;
131
132  if(NULL != errstr)
133    *errstr = g_strdup_printf(_("Failed to create the directory %s:\n%s"),
134                              gl_confdir, strerror(errno));
135  return FALSE;
136}
137
138/* errstr may be NULL, this might be called before GTK is initialized */
139gboolean
140cf_lock(char **errstr) {
141  char *path = g_build_filename(gl_old_confdir, OLD_FILE_LOCK, NULL);
142  int fd = lockfile(path, errstr);
143
144  if(0 > fd)
145    g_free(path);
146  else {
147    gl_old_lockpath = path;
148    path = g_build_filename(gl_confdir, FILE_LOCK, NULL);
149    fd = lockfile(path, errstr);
150    if(0 > fd)
151      g_free(path);
152    else
153      gl_lockpath = path;
154  }
155
156  g_atexit(cf_removelocks);
157
158  return 0 <= fd;
159}
160
161static void
162cf_removelocks( void )
163{
164    g_unlink( gl_lockpath );
165    g_free( gl_lockpath );
166    g_unlink( gl_old_lockpath );
167    g_free( gl_old_lockpath );
168}
169
170char *
171cf_sockname(void) {
172  return g_build_filename(gl_confdir, FILE_SOCKET, NULL);
173}
174
175static char *
176cf_readfile(const char *file, const char *oldfile, gsize *len,
177            gboolean *usedold, char **errstr) {
178  char *path;
179  GIOChannel *io;
180  GError *err = NULL;
181  char *ret;
182
183  *errstr = NULL;
184  *usedold = FALSE;
185  ret = NULL;
186  err = NULL;
187  *len = 0;
188
189  path = g_build_filename(gl_confdir, file, NULL);
190  io = g_io_channel_new_file(path, "r", &err);
191  if(NULL != err && g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
192    g_error_free(err);
193    err = NULL;
194    g_free(path);
195    path = g_build_filename(gl_old_confdir, oldfile, NULL);
196    io = g_io_channel_new_file(path, "r", &err);
197    *usedold = TRUE;
198  }
199  if(NULL != err) {
200    if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
201      *errstr = g_strdup_printf(
202        _("Failed to open the file %s for reading:\n%s"), path, err->message);
203    goto done;
204  }
205  g_io_channel_set_encoding(io, NULL, NULL);
206
207  if(G_IO_STATUS_ERROR == g_io_channel_read_to_end(io, &ret, len, &err)) {
208    *errstr = g_strdup_printf(
209      _("Error while reading from the file %s:\n%s"), path, err->message);
210    goto done;
211  }
212
213 done:
214  g_free (path);
215  if(NULL != err)
216    g_error_free(err);
217  if(NULL != io) 
218    g_io_channel_unref(io);
219  return ret;
220}
221
222/**
223***  Prefs Files
224**/
225
226#define DEFAULT_GROUP "general"
227
228static char*
229getPrefsFilename( void )
230{
231    return g_build_filename( tr_getPrefsDirectory(),
232                             CONF_SUBDIR, "prefs.ini", NULL );
233}
234
235static GKeyFile*
236getPrefsKeyFile( void )
237{
238    static GKeyFile * myKeyFile = NULL;
239
240    if( myKeyFile == NULL )
241    {
242        char * filename = getPrefsFilename( );
243        myKeyFile = g_key_file_new( );
244        g_key_file_load_from_file( myKeyFile, filename, 0, NULL );
245        g_free( filename );
246    }
247
248    return myKeyFile;
249}
250
251int
252pref_int_get( const char * key )
253{
254    return g_key_file_get_integer( getPrefsKeyFile( ), DEFAULT_GROUP, key, NULL );
255}
256void
257pref_int_set( const char * key, int value )
258{
259    g_key_file_set_integer( getPrefsKeyFile( ), DEFAULT_GROUP, key, value );
260}
261void
262pref_int_set_default( const char * key, int value )
263{
264    if( !g_key_file_has_key( getPrefsKeyFile( ), DEFAULT_GROUP, key, NULL ) )
265        pref_int_set( key, value );
266}
267
268gboolean
269pref_flag_get ( const char * key )
270{
271    return g_key_file_get_boolean( getPrefsKeyFile( ), DEFAULT_GROUP, key, NULL );
272}
273void
274pref_flag_set( const char * key, gboolean value )
275{
276    g_key_file_set_boolean( getPrefsKeyFile( ), DEFAULT_GROUP, key, value );
277}
278void
279pref_flag_set_default( const char * key, gboolean value )
280{
281    if( !g_key_file_has_key( getPrefsKeyFile( ), DEFAULT_GROUP, key, NULL ) )
282        pref_flag_set( key, value );
283}
284
285char*
286pref_string_get( const char * key )
287{
288    return g_key_file_get_string( getPrefsKeyFile( ), DEFAULT_GROUP, key, NULL );
289}
290void
291pref_string_set( const char * key, const char * value )
292{
293    g_key_file_set_string( getPrefsKeyFile( ), DEFAULT_GROUP, key, value );
294}
295void
296pref_string_set_default( const char * key, const char * value )
297{
298    if( !g_key_file_has_key( getPrefsKeyFile( ), DEFAULT_GROUP, key, NULL ) )
299        pref_string_set( key, value );
300}
301
302void
303pref_save(char **errstr)
304{
305    gsize datalen;
306    GError * err = NULL;
307    char * data;
308    char * filename;
309    char * path;
310
311    filename = getPrefsFilename( );
312    path = g_path_get_dirname( filename );
313    mkdir_p( path, 0755 );
314
315    data = g_key_file_to_data( getPrefsKeyFile(), &datalen, &err );
316    if( !err ) {
317        GIOChannel * out = g_io_channel_new_file( filename, "w+", &err );
318        g_io_channel_write_chars( out, data, datalen, NULL, &err );
319        g_io_channel_unref( out );
320    }
321
322    if( errstr != NULL )
323        *errstr = err ? g_strdup( err->message ) : NULL;
324
325    g_clear_error( &err );
326    g_free( data );
327    g_free( path );
328    g_free( filename );
329}
330
331/**
332***
333**/
334
335benc_val_t *
336cf_loadstate(char **errstr) {
337  char *data, *line, *eol, *prog;
338  gsize len;
339  gboolean usedold;
340  benc_val_t *state, *torstate;
341
342  *errstr = NULL;
343
344  data = cf_readfile(FILE_STATE, OLD_FILE_STATE, &len, &usedold, errstr);
345  if(NULL != *errstr) {
346    g_assert(NULL == data);
347    return NULL;
348  }
349
350  if(NULL == data)
351    return NULL;
352
353  state = g_new0(benc_val_t, 1);
354  if(usedold || tr_bencLoad(data, len, state, NULL)) {
355    /* XXX all this evil compat code should go away at some point */
356    memset(state, 0,  sizeof(benc_val_t));
357    state->type = TYPE_LIST;
358    for(line = data; NULL != (eol = strchr(line, PREF_SEP_LINE));
359        line = eol + 1) {
360      *eol = '\0';
361      if(g_utf8_validate(line, -1, NULL)) {
362        cf_benc_append(state, TYPE_DICT, 10);
363        torstate = state->val.l.vals + state->val.l.count - 1;
364        prog = line;
365        while(NULL != (prog = getstateval(torstate, prog)))
366          ;
367      }
368    }
369  }
370
371  g_free(data);
372
373  return state;
374}
375
376static void
377cf_benc_append(benc_val_t *val, char type, int incsize) {
378  if(++val->val.l.count > val->val.l.alloc) {
379    val->val.l.alloc += incsize;
380    val->val.l.vals = g_renew(benc_val_t, val->val.l.vals, val->val.l.alloc);
381    memset(val->val.l.vals + val->val.l.alloc - incsize, 0,
382          incsize * sizeof(benc_val_t));
383  }
384  val->val.l.vals[val->val.l.count-1].type = type;
385}
386
387static void
388cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
389             char **errstr) {
390  char *path = g_build_filename(gl_confdir, file, NULL);
391  char *pathtmp = g_build_filename(gl_confdir, tmp, NULL);
392  GIOChannel *io = NULL;
393  GError *err = NULL;
394  char *datastr;
395  int len;
396  gsize written;
397
398  *errstr = NULL;
399  err = NULL;
400  datastr = NULL;
401
402  io = g_io_channel_new_file(pathtmp, "w", &err);
403  if(NULL != err) {
404    *errstr = g_strdup_printf(_("Failed to open the file %s for writing:\n%s"),
405                              pathtmp, err->message);
406    goto done;
407  }
408  g_io_channel_set_encoding(io, NULL, NULL);
409
410  len = 0;
411  datastr = tr_bencSaveMalloc(data, &len);
412
413  written = 0;
414  g_io_channel_write_chars(io, datastr, len, &written, &err);
415  if(NULL != err)
416    g_io_channel_flush(io, &err);
417  if(NULL != err) {
418    *errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
419                              pathtmp, err->message);
420    goto done;
421  }
422
423  if(0 > rename(pathtmp, path)) {
424    *errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
425                              pathtmp, file, strerror(errno));
426    goto done;
427  }
428
429 done:
430  g_free(path);
431  g_free(pathtmp);
432  if(NULL != io)
433    g_io_channel_unref(io);
434  if(NULL != datastr)
435    free(datastr);
436}
437
438static gboolean
439strbool( const char * str )
440{
441  if( !str )
442    return FALSE;
443
444  switch(str[0]) {
445    case 'y': case 't': case 'Y': case '1': case 'j': case 'e':
446      return TRUE;
447    default:
448      if(0 == g_ascii_strcasecmp("on", str))
449        return TRUE;
450      break;
451  }
452
453  return FALSE;
454}
455
456
457static char *
458getstateval(benc_val_t *state, char *line) {
459  char *start, *end;
460
461  /* skip any leading whitespace */
462  while(g_ascii_isspace(*line))
463    line++;
464
465  /* walk over the key, which may be alphanumerics as well as - or _ */
466  for(start = line; g_ascii_isalnum(*start)
467        || '_' == *start || '-' == *start; start++)
468    ;
469
470  /* they key must be immediately followed by an = */
471  if('=' != *start)
472    return NULL;
473  *(start++) = '\0';
474
475  /* then the opening quote for the value */
476  if('"' != *(start++))
477    return NULL;
478
479  /* walk over the value */
480  for(end = start; '\0' != *end && '"' != *end; end++)
481    /* skip over escaped quotes */
482    if('\\' == *end && '\0' != *(end + 1))
483      end++;
484
485  /* make sure we didn't hit the end of the string */
486  if('"' != *end)
487    return NULL;
488  *end = '\0';
489
490  /* if it's a key we recognize then save the data */
491  if(0 == strcmp(line, "torrent") || 0 == strcmp(line, "dir") ||
492     0 == strcmp(line, "paused")) {
493    cf_benc_append(state, TYPE_STR, 6);
494    state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(line);
495    state->val.l.vals[state->val.l.count-1].val.s.i = strlen(line);
496    if('p' == *line) {
497      cf_benc_append(state, TYPE_INT, 6);
498      state->val.l.vals[state->val.l.count-1].val.i = strbool(start);
499    } else {
500      cf_benc_append(state, TYPE_STR, 6);
501      state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(start);
502      state->val.l.vals[state->val.l.count-1].val.s.i = strlen(start);
503    }
504  }
505
506  /* return a pointer to just past the end of the value */
507  return end + 1;
508}
509
510void
511cf_savestate(benc_val_t *state, char **errstr) {
512  *errstr = NULL;
513  cf_writebenc(FILE_STATE, FILE_STATE_TMP, state, errstr);
514}
515
516void
517cf_freestate( benc_val_t * state )
518{
519    if( NULL != state )
520    {
521        tr_bencFree( state );
522        g_free( state );
523    }
524}
Note: See TracBrowser for help on using the repository browser.