source: branches/1.5x/gtk/conf.c @ 8048

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

(1.5x gtk) launchpad bug #338046: XDG_DOWNLOAD_DIR isn't honored in gtk client

  • Property svn:keywords set to Date Rev Author Id
File size: 14.5 KB
Line 
1/******************************************************************************
2 * $Id: conf.c 8048 2009-03-10 15:24:41Z 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 "tr-prefs.h"
43#include "util.h"
44
45#define MY_NAME "transmission"
46
47static char * gl_confdir = NULL;
48static char * gl_lockpath = NULL;
49
50/* errstr may be NULL, this might be called before GTK is initialized */
51gboolean
52cf_init( const char   * configDir,
53         char        ** errstr )
54{
55    if( errstr != NULL )
56        *errstr = NULL;
57
58    gl_confdir = g_strdup( configDir );
59
60    if( mkdir_p( gl_confdir, 0755 ) )
61        return TRUE;
62
63    if( errstr != NULL )
64        *errstr = g_strdup_printf( _( "Couldn't create \"%1$s\": %2$s" ),
65                                  gl_confdir, g_strerror( errno ) );
66
67    return FALSE;
68}
69
70/***
71****
72****  Lockfile
73****
74***/
75
76/* errstr may be NULL, this might be called before GTK is initialized */
77static gboolean
78lockfile( const char *         filename,
79          tr_lockfile_state_t *tr_state,
80          char **              errstr )
81{
82    const tr_lockfile_state_t state = tr_lockfile( filename );
83    const gboolean            success = state == TR_LOCKFILE_SUCCESS;
84
85    if( errstr ) switch( state )
86        {
87            case TR_LOCKFILE_EOPEN:
88                *errstr =
89                    g_strdup_printf( _( "Couldn't open \"%1$s\": %2$s" ),
90                                    filename, g_strerror( errno ) );
91                break;
92
93            case TR_LOCKFILE_ELOCK:
94                *errstr = g_strdup_printf( _( "%s is already running." ),
95                                          g_get_application_name( ) );
96                break;
97
98            case TR_LOCKFILE_SUCCESS:
99                *errstr = NULL;
100                break;
101        }
102
103    if( tr_state != NULL )
104        *tr_state = state;
105
106    return success;
107}
108
109static char*
110getLockFilename( void )
111{
112    assert( gl_confdir != NULL );
113    return g_build_filename( gl_confdir, "lock", NULL );
114}
115
116static void
117cf_removelocks( void )
118{
119    if( gl_lockpath )
120    {
121        g_unlink( gl_lockpath );
122        g_free( gl_lockpath );
123    }
124}
125
126/* errstr may be NULL, this might be called before GTK is initialized */
127gboolean
128cf_lock( tr_lockfile_state_t *tr_state,
129         char **              errstr )
130{
131    char *         path = getLockFilename( );
132    const gboolean didLock = lockfile( path, tr_state, errstr );
133
134    if( didLock )
135        gl_lockpath = g_strdup( path );
136    g_atexit( cf_removelocks );
137    g_free( path );
138    return didLock;
139}
140
141/***
142****
143****  Preferences
144****
145***/
146
147static void cf_check_older_configs( void );
148
149/**
150 * This is where we initialize the preferences file with the default values.
151 * If you add a new preferences key, you /must/ add a default value here.
152 */
153static void
154tr_prefs_init_defaults( tr_benc * d )
155{
156    const char * str;
157
158    cf_check_older_configs( );
159
160#ifdef HAVE_GIO
161    str = NULL;
162    if( !str ) str = g_get_user_special_dir( G_USER_DIRECTORY_DOWNLOAD );
163    if( !str ) str = g_get_user_special_dir( G_USER_DIRECTORY_DESKTOP );
164    if( !str ) str = tr_getDefaultDownloadDir( );
165    tr_bencDictAddStr( d, PREF_KEY_DIR_WATCH, str );
166    tr_bencDictAddInt( d, PREF_KEY_DIR_WATCH_ENABLED, FALSE );
167#endif
168
169    tr_bencDictAddInt( d, PREF_KEY_INHIBIT_HIBERNATION, FALSE );
170    tr_bencDictAddInt( d, PREF_KEY_BLOCKLIST_UPDATES_ENABLED, TRUE );
171
172    tr_bencDictAddStr( d, PREF_KEY_OPEN_DIALOG_FOLDER, g_get_home_dir( ) );
173
174    tr_bencDictAddInt( d, PREF_KEY_TOOLBAR, TRUE );
175    tr_bencDictAddInt( d, PREF_KEY_FILTERBAR, TRUE );
176    tr_bencDictAddInt( d, PREF_KEY_STATUSBAR, TRUE );
177    tr_bencDictAddInt( d, PREF_KEY_SHOW_TRAY_ICON, FALSE );
178    tr_bencDictAddInt( d, PREF_KEY_SHOW_DESKTOP_NOTIFICATION, TRUE );
179    tr_bencDictAddStr( d, PREF_KEY_STATUSBAR_STATS, "total-ratio" );
180
181    tr_bencDictAddInt( d, PREF_KEY_SCHED_LIMIT_ENABLED, FALSE );
182    tr_bencDictAddInt( d, PREF_KEY_SCHED_BEGIN,    60 * 23 ); /* 11pm */
183    tr_bencDictAddInt( d, PREF_KEY_SCHED_END,      60 * 7 );  /* 7am */
184    tr_bencDictAddInt( d, PREF_KEY_SCHED_DL_LIMIT, 200 );   /* 2x the other limit */
185    tr_bencDictAddInt( d, PREF_KEY_SCHED_UL_LIMIT, 100 );   /* 2x the other limit */
186
187    tr_bencDictAddInt( d, PREF_KEY_OPTIONS_PROMPT, TRUE );
188
189    tr_bencDictAddInt( d, PREF_KEY_MAIN_WINDOW_HEIGHT, 500 );
190    tr_bencDictAddInt( d, PREF_KEY_MAIN_WINDOW_WIDTH, 300 );
191    tr_bencDictAddInt( d, PREF_KEY_MAIN_WINDOW_X, 50 );
192    tr_bencDictAddInt( d, PREF_KEY_MAIN_WINDOW_Y, 50 );
193    tr_bencDictAddStr( d, PREF_KEY_MAIN_WINDOW_LAYOUT_ORDER, "menu,toolbar,filter,list,statusbar" );
194
195    str = NULL;
196#if GLIB_CHECK_VERSION( 2, 14, 0 )
197    if( !str ) str = g_get_user_special_dir( G_USER_DIRECTORY_DOWNLOAD );
198#endif
199    if( !str ) str = tr_getDefaultDownloadDir( );
200    tr_bencDictAddStr( d, TR_PREFS_KEY_DOWNLOAD_DIR, str );
201
202    tr_bencDictAddInt( d, PREF_KEY_ASKQUIT, TRUE );
203
204    tr_bencDictAddStr( d, PREF_KEY_SORT_MODE, "sort-by-name" );
205    tr_bencDictAddInt( d, PREF_KEY_SORT_REVERSED, FALSE );
206    tr_bencDictAddInt( d, PREF_KEY_MINIMAL_VIEW, FALSE );
207
208    tr_bencDictAddInt( d, PREF_KEY_START, TRUE );
209    tr_bencDictAddInt( d, PREF_KEY_TRASH_ORIGINAL, FALSE );
210}
211
212static char*
213getPrefsFilename( void )
214{
215    assert( gl_confdir != NULL );
216    return g_build_filename( gl_confdir, "settings.json", NULL );
217}
218
219static tr_benc*
220getPrefs( void )
221{
222    static tr_benc settings;
223    static gboolean loaded = FALSE;
224
225    if( !loaded )
226    {
227        tr_bencInitDict( &settings, 0 );
228        tr_prefs_init_defaults( &settings );
229        tr_sessionLoadSettings( &settings, gl_confdir, MY_NAME );
230        loaded = TRUE;
231    }
232
233    return &settings;
234}
235
236/***
237****
238***/
239
240tr_benc*
241pref_get_all( void )
242{
243    return getPrefs( );
244}
245
246int64_t
247pref_int_get( const char * key )
248{
249    int64_t i = 0;
250
251    tr_bencDictFindInt( getPrefs( ), key, &i );
252    return i;
253}
254
255void
256pref_int_set( const char * key,
257              int64_t      value )
258{
259    tr_bencDictAddInt( getPrefs( ), key, value );
260}
261
262/***
263****
264***/
265
266gboolean
267pref_flag_get( const char * key )
268{
269    int64_t i;
270
271    tr_bencDictFindInt( getPrefs( ), key, &i );
272    return i != 0;
273}
274
275gboolean
276pref_flag_eval( pref_flag_t  val,
277                const char * key )
278{
279    switch( val )
280    {
281        case PREF_FLAG_TRUE:
282            return TRUE;
283
284        case PREF_FLAG_FALSE:
285            return FALSE;
286
287        default:
288            return pref_flag_get( key );
289    }
290}
291
292void
293pref_flag_set( const char * key,
294               gboolean     value )
295{
296    pref_int_set( key, value != 0 );
297}
298
299/***
300****
301***/
302
303const char*
304pref_string_get( const char * key )
305{
306    const char * str = NULL;
307    tr_bencDictFindStr( getPrefs( ), key, &str );
308    return str;
309}
310
311void
312pref_string_set( const char * key, const char * value )
313{
314    tr_bencDictAddStr( getPrefs( ), key, value );
315}
316
317/***
318****
319***/
320
321void
322pref_save( tr_session * session )
323{
324    tr_sessionSaveSettings( session, gl_confdir, getPrefs( ) );
325}
326
327/***
328****
329***/
330
331#if !GLIB_CHECK_VERSION( 2, 8, 0 )
332static void
333tr_file_set_contents( const char *   filename,
334                      const void *   out,
335                      size_t         len,
336                      GError* unused UNUSED )
337{
338    FILE * fp = fopen( filename, "wb+" );
339
340    if( fp != NULL )
341    {
342        fwrite( out, 1, len, fp );
343        fclose( fp );
344    }
345}
346
347 #define g_file_set_contents tr_file_set_contents
348#endif
349
350static char*
351getCompat080PrefsFilename( void )
352{
353    assert( gl_confdir != NULL );
354
355    return g_build_filename( g_get_home_dir( ), ".transmission", "gtk", "prefs", NULL );
356}
357
358static char*
359getCompat090PrefsFilename( void )
360{
361    assert( gl_confdir != NULL );
362
363    return g_build_filename( g_get_home_dir( ), ".transmission", "gtk", "prefs.ini", NULL );
364}
365
366static char*
367getCompat121PrefsFilename( void )
368{
369    return g_build_filename( g_get_user_config_dir( ), "transmission", "gtk", "prefs.ini", NULL );
370}
371
372static void
373translate_08_to_09( const char* oldfile,
374                    const char* newfile )
375{
376    static struct pref_entry {
377        const char*   oldkey;
378        const char*   newkey;
379    } pref_table[] = {
380        { "add-behavior-ipc",       "add-behavior-ipc"               },
381        { "add-behavior-standard",  "add-behavior-standard"          },
382        { "download-directory",     "default-download-directory"     },
383        { "download-limit",         "download-limit"                 },
384        { "use-download-limit",     "download-limit-enabled"         },
385        { "listening-port",         "listening-port"                 },
386        { "use-nat-traversal",      "nat-traversal-enabled"          },
387        { "use-peer-exchange",      "pex-enabled"                    },
388        { "ask-quit",               "prompt-before-exit"             },
389        { "ask-download-directory", "prompt-for-download-directory"  },
390        { "use-tray-icon",          "system-tray-icon-enabled"       },
391        { "upload-limit",           "upload-limit"                   },
392        { "use-upload-limit",       "upload-limit-enabled"           }
393    };
394
395    GString * out = g_string_new( NULL );
396    gchar *   contents = NULL;
397    gsize     contents_len = 0;
398    tr_benc   top;
399
400    memset( &top, 0, sizeof( tr_benc ) );
401
402    if( g_file_get_contents( oldfile, &contents, &contents_len, NULL )
403      && !tr_bencLoad( contents, contents_len, &top, NULL )
404      && top.type == TYPE_DICT )
405    {
406        unsigned int i;
407        g_string_append( out, "\n[general]\n" );
408        for( i = 0; i < G_N_ELEMENTS( pref_table ); ++i )
409        {
410            const tr_benc * val = tr_bencDictFind( &top,
411                                                   pref_table[i].oldkey );
412            if( val != NULL )
413            {
414                const char * valstr = val->val.s.s;
415                if( !strcmp( valstr, "yes" ) ) valstr = "true";
416                if( !strcmp( valstr, "no" ) ) valstr = "false";
417                g_string_append_printf( out, "%s=%s\n",
418                                        pref_table[i].newkey,
419                                        valstr );
420            }
421        }
422    }
423
424    g_file_set_contents( newfile, out->str, out->len, NULL );
425    g_string_free( out, TRUE );
426    g_free( contents );
427}
428
429static void
430translate_keyfile_to_json( const char * old_file,
431                           const char * new_file )
432{
433    tr_benc    dict;
434    GKeyFile * keyfile;
435    gchar **   keys;
436    gsize      i;
437    gsize      length;
438
439    static struct pref_entry {
440        const char*   oldkey;
441        const char*   newkey;
442    } renamed[] = {
443        { "default-download-directory", "download-dir"             },
444        { "encrypted-connections-only", "encryption"               },
445        { "listening-port",             "peer-port"                },
446        { "nat-traversal-enabled",      "port-forwarding-enabled"  },
447        { "open-dialog-folder",         "open-dialog-dir"          },
448        { "watch-folder",               "watch-dir"                },
449        { "watch-folder-enabled",       "watch-dir-enabled"        }
450    };
451
452    keyfile = g_key_file_new( );
453    g_key_file_load_from_file( keyfile, old_file, 0, NULL );
454    length = 0;
455    keys = g_key_file_get_keys( keyfile, "general", &length, NULL );
456
457    tr_bencInitDict( &dict, length );
458    for( i = 0; i < length; ++i )
459    {
460        guint        j;
461        const char * key = keys[i];
462        gchar *      val = g_key_file_get_value( keyfile, "general", key,
463                                                 NULL );
464
465        for( j = 0; j < G_N_ELEMENTS( renamed ); ++j )
466            if( !strcmp( renamed[j].oldkey, key ) )
467                key = renamed[j].newkey;
468
469        if( !strcmp( val, "true" ) || !strcmp( val, "false" ) )
470            tr_bencDictAddInt( &dict, key, !strcmp( val, "true" ) );
471        else
472        {
473            char * end;
474            long   l;
475            errno = 0;
476            l = strtol( val, &end, 10 );
477            if( !errno && end && !*end )
478                tr_bencDictAddInt( &dict, key, l );
479            else
480                tr_bencDictAddStr( &dict, key, val );
481        }
482
483        g_free( val );
484    }
485
486    g_key_file_free( keyfile );
487    tr_bencSaveJSONFile( new_file, &dict );
488    tr_bencFree( &dict );
489}
490
491static void
492cf_check_older_configs( void )
493{
494    char * filename = getPrefsFilename( );
495
496    if( !g_file_test( filename, G_FILE_TEST_IS_REGULAR ) )
497    {
498        char * key1 = getCompat121PrefsFilename( );
499        char * key2 = getCompat090PrefsFilename( );
500        char * benc = getCompat080PrefsFilename( );
501
502        if( g_file_test( key1, G_FILE_TEST_IS_REGULAR ) )
503        {
504            g_message( _( "Importing \"%s\"" ), key1 );
505            translate_keyfile_to_json( key1, filename );
506        }
507        else if( g_file_test( key2, G_FILE_TEST_IS_REGULAR ) )
508        {
509            g_message( _( "Importing \"%s\"" ), key2 );
510            translate_keyfile_to_json( key2, filename );
511        }
512        else if( g_file_test( benc, G_FILE_TEST_IS_REGULAR ) )
513        {
514            char * tmpfile;
515            int    fd = g_file_open_tmp( "transmission-prefs-XXXXXX", &tmpfile, NULL );
516            g_message( _( "Importing \"%s\"" ), benc );
517            if( fd != -1 ) close( fd );
518            translate_08_to_09( benc, tmpfile );
519            translate_keyfile_to_json( tmpfile, filename );
520            unlink( tmpfile );
521        }
522
523        g_free( benc );
524        g_free( key2 );
525        g_free( key1 );
526    }
527
528    g_free( filename );
529}
Note: See TracBrowser for help on using the repository browser.