source: trunk/gtk/conf.c @ 6166

Last change on this file since 6166 was 6166, checked in by charles, 13 years ago

make the preference file keys more consistent.

  • Property svn:keywords set to Date Rev Author Id
File size: 11.1 KB
Line 
1/******************************************************************************
2 * $Id: conf.c 6166 2008-06-13 00:52:55Z charles $
3 *
4 * Copyright (c) 2005-2008 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 <assert.h>
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include <unistd.h>
32
33#include <glib.h>
34#include <glib/gi18n.h>
35#include <glib/gstdio.h>
36
37#include <libtransmission/transmission.h>
38#include <libtransmission/bencode.h>
39#include <libtransmission/platform.h>
40
41#include "conf.h"
42#include "util.h"
43
44static char * gl_confdir = NULL;
45static char * gl_lockpath = NULL;
46
47/* errstr may be NULL, this might be called before GTK is initialized */
48gboolean
49cf_init(const char *dir, char **errstr)
50{
51    if( errstr != NULL )
52        *errstr = NULL;
53
54    gl_confdir = g_strdup( dir );
55
56    if( mkdir_p( gl_confdir, 0755 ) )
57        return TRUE;
58
59    if( errstr != NULL )
60        *errstr = g_strdup_printf( _( "Couldn't create \"%1$s\": %2$s" ),
61                                   gl_confdir, g_strerror(errno) );
62
63    return FALSE;
64}
65
66/***
67****
68****  Lockfile
69****
70***/
71
72/* errstr may be NULL, this might be called before GTK is initialized */
73static gboolean
74lockfile(const char * filename, char **errstr)
75{
76    const int state = tr_lockfile( filename );
77    const gboolean success = state == TR_LOCKFILE_SUCCESS;
78
79    if( errstr ) switch( state ) {
80        case TR_LOCKFILE_EOPEN:
81        *errstr = g_strdup_printf( _( "Couldn't open \"%1$s\": %2$s" ),
82                                   filename, g_strerror( errno ) );
83            break;
84        case TR_LOCKFILE_ELOCK:
85            *errstr = g_strdup_printf( _( "%s is already running." ),
86                                       g_get_application_name( ) );
87            break;
88        case TR_LOCKFILE_SUCCESS:
89            *errstr = NULL;
90            break;
91    }
92
93    return success;
94}
95
96static char*
97getLockFilename( void )
98{
99    assert( gl_confdir != NULL );
100    return g_build_filename( gl_confdir, "lock", NULL );
101}
102
103static void
104cf_removelocks( void )
105{
106    if( gl_lockpath ) {
107      g_unlink( gl_lockpath );
108      g_free( gl_lockpath );
109    }
110}
111
112/* errstr may be NULL, this might be called before GTK is initialized */
113gboolean
114cf_lock( char ** errstr )
115{
116    char * path = getLockFilename( );
117    const gboolean didLock = lockfile( path, errstr );
118    if( didLock )
119        gl_lockpath = g_strdup( path );
120    g_atexit( cf_removelocks );
121    g_free( path );
122    return didLock;
123}
124
125/***
126****
127****  Preferences
128****
129***/
130
131static char*
132getPrefsFilename( void )
133{
134    assert( gl_confdir != NULL );
135    return g_build_filename( gl_confdir, "settings.json", NULL );
136}
137
138static tr_benc*
139getPrefs( void )
140{
141    static tr_benc dict;
142    static gboolean loaded = FALSE;
143
144    if( !loaded )
145    {
146        char * filename = getPrefsFilename( );
147        if( tr_bencLoadJSONFile( filename, &dict ) )
148            tr_bencInitDict( &dict, 100 );
149        g_free( filename );
150        loaded = TRUE;
151    }
152
153    return &dict;
154}
155
156/***
157****
158***/
159
160int
161pref_int_get( const char * key )
162{
163    int64_t i;
164    tr_bencDictFindInt( getPrefs( ), key, &i );
165    return i;
166}
167void
168pref_int_set( const char * key, int value )
169{
170    tr_benc * d = getPrefs( );
171    tr_bencDictRemove( d, key );
172    tr_bencDictAddInt( d, key, value );
173}
174void
175pref_int_set_default( const char * key, int value )
176{
177    if( !tr_bencDictFind( getPrefs( ), key ) )
178        pref_int_set( key, value );
179}
180
181/***
182****
183***/
184
185gboolean
186pref_flag_get ( const char * key )
187{
188    int64_t i;
189    tr_bencDictFindInt( getPrefs( ), key, &i );
190    return i != 0;
191}
192gboolean
193pref_flag_eval( pref_flag_t val, const char * key )
194{
195    switch( val ) {
196        case PREF_FLAG_TRUE: return TRUE;
197        case PREF_FLAG_FALSE: return FALSE;
198        default: return pref_flag_get( key );
199    }
200}
201void
202pref_flag_set( const char * key, gboolean value )
203{
204    pref_int_set( key, value!=0 );
205}
206void
207pref_flag_set_default( const char * key, gboolean value )
208{
209    pref_int_set_default( key, value!=0 );
210}
211
212/***
213****
214***/
215
216const char*
217pref_string_get( const char * key )
218{
219    const char * str = NULL;
220    tr_bencDictFindStr( getPrefs( ), key, &str );
221    return str;
222}
223
224void
225pref_string_set( const char * key, const char * value )
226{
227    tr_benc * d = getPrefs( );
228    tr_bencDictRemove( d, key );
229    tr_bencDictAddStr( d, key, value );
230}
231
232void
233pref_string_set_default( const char * key, const char * value )
234{
235    if( !tr_bencDictFind( getPrefs( ), key ) )
236        pref_string_set( key, value );
237}
238
239/***
240****
241***/
242
243void
244pref_save( void )
245{
246    char * filename = getPrefsFilename( );
247    char * path = g_path_get_dirname( filename );
248
249    mkdir_p( path, 0755 );
250    tr_bencSaveJSONFile( filename, getPrefs( ) );
251
252    g_free( path );
253    g_free( filename );
254}
255
256/***
257****
258***/
259
260#if !GLIB_CHECK_VERSION(2,8,0)
261static void
262tr_file_set_contents( const char * filename, const void * out, size_t len, GError* unused UNUSED )
263{
264    FILE * fp = fopen( filename, "wb+" );
265    if( fp != NULL ) {
266        fwrite( out, 1, len, fp );
267        fclose( fp );
268    }
269}
270#define g_file_set_contents tr_file_set_contents
271#endif
272
273static char*
274getCompat080PrefsFilename( void )
275{
276    assert( gl_confdir != NULL );
277    return g_build_filename( g_get_home_dir(), ".transmission", "gtk", "prefs", NULL );
278}
279
280static char*
281getCompat090PrefsFilename( void )
282{
283    assert( gl_confdir != NULL );
284    return g_build_filename( g_get_home_dir(), ".transmission", "gtk", "prefs.ini", NULL );
285}
286
287static char*
288getCompat121PrefsFilename( void )
289{
290    return g_build_filename( g_get_user_config_dir(), "transmission", "gtk", "prefs.ini", NULL );
291}
292
293static void
294translate_08_to_09( const char* oldfile, const char* newfile )
295{
296    static struct pref_entry {
297        const char* oldkey;
298        const char* newkey;
299    } pref_table[] = {
300        { "add-behavior-ipc",       "add-behavior-ipc"},
301        { "add-behavior-standard",  "add-behavior-standard"},
302        { "download-directory",     "default-download-directory"},
303        { "download-limit",         "download-limit"},
304        { "use-download-limit",     "download-limit-enabled" },
305        { "listening-port",         "listening-port"},
306        { "use-nat-traversal",      "nat-traversal-enabled"},
307        { "use-peer-exchange",      "pex-enabled"},
308        { "ask-quit",               "prompt-before-exit"},
309        { "ask-download-directory", "prompt-for-download-directory"},
310        { "use-tray-icon",          "system-tray-icon-enabled"},
311        { "upload-limit",           "upload-limit"},
312        { "use-upload-limit",       "upload-limit-enabled"}
313    };
314
315    GString * out = g_string_new( NULL );
316    gchar * contents = NULL;
317    gsize contents_len = 0;
318    tr_benc top;
319
320    memset( &top, 0, sizeof(tr_benc) );
321
322    if( g_file_get_contents( oldfile, &contents, &contents_len, NULL )
323        && !tr_bencLoad( contents, contents_len, &top, NULL )
324        && top.type==TYPE_DICT )
325    {
326        unsigned int i;
327        g_string_append( out, "\n[general]\n" );
328        for ( i=0; i<G_N_ELEMENTS(pref_table); ++i ) {
329            const tr_benc * val = tr_bencDictFind( &top, pref_table[i].oldkey );
330            if( val != NULL ) {
331                const char * valstr = val->val.s.s;
332                if( !strcmp( valstr, "yes" ) ) valstr = "true";
333                if( !strcmp( valstr, "no" ) ) valstr = "false";
334                g_string_append_printf( out, "%s=%s\n", pref_table[i].newkey, valstr );
335            }
336        }
337    }
338
339    g_file_set_contents( newfile, out->str, out->len, NULL );
340    g_string_free( out, TRUE );
341    g_free( contents );
342}
343
344static void
345translate_keyfile_to_json( const char * old_file, const char * new_file )
346{
347    tr_benc dict;
348    GKeyFile * keyfile;
349    gchar ** keys;
350    gsize i;
351    gsize length;
352
353    static struct pref_entry {
354        const char* oldkey;
355        const char* newkey;
356    } renamed[] = {
357        { "default-download-directory", "download-dir" },
358        { "encrypted-connections-only", "encryption" },
359        { "listening-port", "peer-port" },
360        { "nat-traversal-enabled", "port-forwarding-enabled" },
361        { "open-dialog-folder", "open-dialog-dir" },
362        { "watch-folder", "watch-dir" },
363        { "watch-folder-enabled", "watch-dir-enabled" }
364    };
365
366    keyfile = g_key_file_new( );
367    g_key_file_load_from_file( keyfile, old_file, 0, NULL );
368    length = 0;
369    keys = g_key_file_get_keys( keyfile, "general", &length, NULL );
370
371    tr_bencInitDict( &dict, length );
372    for( i=0; i<length; ++i )
373    {
374        guint j;
375        const char * key = keys[i];
376        gchar * val = g_key_file_get_value( keyfile, "general", key, NULL );
377
378        for( j=0; j<G_N_ELEMENTS(renamed); ++j )
379            if( !strcmp( renamed[j].oldkey, key ) )
380                key = renamed[j].newkey;
381
382        if( !strcmp(val,"true") || !strcmp(val,"false") )
383            tr_bencDictAddInt( &dict, key, !strcmp( val, "true" ) );
384        else {
385            char * end;
386            long l;
387            errno = 0;
388            l = strtol( val, &end, 10 );
389            if( !errno && end && !*end )
390                tr_bencDictAddInt( &dict, key, l );
391            else
392                tr_bencDictAddStr( &dict, key, val );
393        }
394
395        g_free( val );
396    }
397
398    g_key_file_free( keyfile );
399    tr_bencSaveJSONFile( new_file, &dict );
400    tr_bencFree( &dict );
401}
402
403void
404cf_check_older_configs( void )
405{
406    char * filename = getPrefsFilename( );
407
408    if( !g_file_test( filename, G_FILE_TEST_IS_REGULAR ) )
409    {
410        char * key1 = getCompat121PrefsFilename( );
411        char * key2 = getCompat090PrefsFilename( );
412        char * benc = getCompat080PrefsFilename( );
413
414        if( g_file_test( key1, G_FILE_TEST_IS_REGULAR ) )
415        {
416            translate_keyfile_to_json( key1, filename );
417        }
418        else if( g_file_test( key2, G_FILE_TEST_IS_REGULAR ) )
419        {
420            translate_keyfile_to_json( key2, filename );
421        }
422        else if( g_file_test( benc, G_FILE_TEST_IS_REGULAR ) )
423        {
424            char * tmpfile;
425            int fd = g_file_open_tmp( "transmission-prefs-XXXXXX", &tmpfile, NULL );
426            if( fd != -1 ) close( fd );
427            translate_08_to_09( benc, tmpfile );
428            translate_keyfile_to_json( tmpfile, filename );
429            unlink( tmpfile );
430        }
431
432        g_free( benc );
433        g_free( key2 );
434        g_free( key1 );
435    }
436
437    g_free( filename );
438}
Note: See TracBrowser for help on using the repository browser.