source: trunk/config.py @ 317

Revision 317, 11.6 KB checked in by marc, 8 years ago (diff)

Improved makefile to work with prefixes, fixed errors, improved manpag and readme, and fix a configuration error

  • Property svn:keywords set to Id Rev
Line 
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Itaka is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3 of the License, or
7# any later version.
8#
9# Itaka is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Itaka; if not, write to the Free Software
16# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17#
18# Copyright 2003-2009 Marc E.
19# http://itaka.jardinpresente.com.ar
20#
21# $Id$
22
23""" Itaka configuration engine """
24
25__version__ = '0.3'
26__revision__ = '$Rev$'
27
28import os
29import platform
30import sys
31import ConfigParser
32import shutil
33import traceback
34
35#: Availability of libnotify
36notify_available = False
37
38try:
39    import pynotify
40    notify_available = True
41
42    if not pynotify.init('Itaka'):
43        print_w(_('Pynotify module is failing, disabling notifications'))
44        notify_available = False
45except ImportError:
46    print_w(_('Pynotify module is missing, disabling notifications'))
47    notify_available = False
48
49config_instance = ConfigParser.ConfigParser()
50image_dir = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'share/images/')
51sound_dir = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'share/sounds/')
52system = os.name
53# We need something more specific
54platform = platform.system()
55
56#: Minimum screen height to display all panes of Itaka
57min_screen_height = 800
58
59#: To be changed on install to specify where the installed files actually are
60image_prefix = '/usr/local/share/itaka/images/'
61if os.path.exists(image_prefix):
62    image_dir = image_prefix
63
64if not os.path.exists(image_dir):
65    print_e(_('Could not find images directory %s' % (image_dir)))
66    sys.exit(1)
67
68#: Save path for screenshots (system-specific specified later on)
69try:
70    save_path = os.getcwd()
71except:
72    print "[*] WARNING: Could not get current directory"
73
74""" Console output verbosity
75'normal' is for all normal operation mesages and warnings (not including errors)
76'debug' is for all messages through self.console.debug
77'quiet' is to quiet all errors and warnings. (totally quiet is in conjunction
78with 'normal' = False, which quiets normal messages too)
79"""
80console_verbosity = {'normal': False, 'debug': False, 'quiet': False}
81
82#: Globally acessable configuration values
83configuration_values = {}
84
85# Second best choice, TMP/TEMP on most systems
86save_path = os.environ.get('TMP') or os.environ.get('TEMP')
87
88# Try APPDATA on Windows or $HOME on POSIX
89if (system == 'nt'):
90    if os.environ.get('APPDATA'):
91                save_path = os.path.join(os.environ.get('APPDATA'), 'itaka')
92    elif os.environ.get('HOME'):
93                save_path = os.path.join(os.environ.get('HOME'), 'itaka')
94else:
95    if os.environ.get('HOME'):
96        save_path = os.path.join(os.environ.get('HOME'), '.itaka')
97
98#: Default HTML headers and footers for the server.
99# Putting <meta http-equiv="Refresh" content="5; url=/"> is very useful for debugging
100head_html = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
101<html>
102<head>
103<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
104<link rel="icon" href="/favicon.ico" type="image/x-icon">
105<title>Itaka</title>
106</head>
107<body>
108<div id="main">
109'''
110
111footer_html = '''
112</div>
113</body>
114</html>'''
115
116class ConfigParser:
117    """
118    Itaka configuration engine
119    """
120
121    def __init__(self, arguments=1):
122        """
123        Configuration engine constructor. It also handles whether the
124        L{console_verbosity} setting is set to debug.
125
126        @type arguments: tuple
127        @param arguments: A tuple of sys.argv
128        """
129
130        if len(arguments) > 1 and arguments[-1] in ('-d', '--debug'):
131            global console_verbosity
132            console_verbosity = {'normal': True, 'debug': True, 'quiet': False}
133            print_m(_('Initializing in debug mode'))
134
135        #: Default configuration sections and values
136        # WARNING: Don't forget to update configuration_dict in uigtk.py if you change this!
137        self.default_options = (
138                {'server': (
139                    ('port', 8000), ('authentication', False),
140                    ('username', 'itaka'), ('password', 'password'),
141                    ('notify', notify_available)
142                )},
143
144                {'screenshot': (
145                    ('format', 'jpeg'), ('quality', 30), ('path', save_path),
146                    ('currentwindow', False), ('scale', False),
147                    ('scalepercent', 100)
148                )},
149               
150                {'log': (
151                    ('logtimeformat', '[%d/%b/%Y %H:%M:%S]'),
152                    ('logfile', '~/.itaka/access.log')
153                )},
154               
155                {'html': (
156                    ('html', '<img src="screenshot" alt="If you are seeing this message it means there was an error in Itaka or you are using a text-only browser.">'),
157                    ('authfailure', '<p><strong>Sorry, but you cannot access this resource without authorization.</strong></p>')
158                )}
159        )
160
161    def load(self):
162        """
163        Set up and load configuration
164
165        @rtype: dict
166        @return: Dictionary of configuration values.
167        """
168
169        self.config_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "itaka.conf")
170
171        global configuration_values
172        configuration_values = {}
173
174        # Check routine
175        if system in ('posix'):
176            if not (os.path.exists(os.path.join(os.environ['HOME'], '.itaka/itaka.conf'))):
177                self.create(os.path.join(os.environ['HOME'], '.itaka/itaka.conf'))
178            else:
179                self.config_file = os.path.join(os.environ['HOME'], '.itaka/itaka.conf')
180        elif (system == 'nt'):
181            if not (os.path.exists(os.path.join(os.environ['APPDATA'], 'Itaka/config.ini'))):
182                self.create(os.path.join(os.environ['APPDATA'], 'Itaka/config.ini'))
183            else:
184                self.config_file = os.path.join(os.environ['APPDATA'], 'Itaka/config.ini')
185        else:
186            # Generic path
187            if not os.path.exists(self.config_file):
188                self.create(self.config_file)
189
190        # Read and assign values from the configuration file
191        try:
192            config_instance.read(self.config_file)
193            if console_verbosity['normal']:
194                print_m(_('Loaded configuration %s') % (self.config_file))
195
196        except:
197            if console_verbosity['normal']: print_e(_('Could not read configuration file (%s)' % (self.config_file)))
198            if console_verbosity['debug']: traceback.print_exc()
199
200        # Get values as a dict and return it
201        for section in config_instance.sections():
202            configuration_values[section] = dict(config_instance.items(section))
203            # Convert 'False' and 'True' into booleans, and numbers into ints
204            # Add config options that are not there
205            for option, value in configuration_values[section].iteritems():
206                if value.strip() == 'True':
207                    configuration_values[section][option] = True
208                elif value.strip() == 'False':
209                    configuration_values[section][option] = False
210                elif value.isdigit():
211                    configuration_values[section][option] = int(value)
212
213        # Compare it to our default configuration set, to see if there is anything missing
214        # This is useful for updates, and corrupted files.
215        # NOTE: The setting of values[section][key] here is purely pragmatical, so we
216        # dont have to reload
217        brokenwarning = False
218        for configdict in self.default_options:
219            for section in configdict:
220                if not configuration_values.has_key(section):
221                    if not console_verbosity['quiet'] and not brokenwarning:
222                        print_w(_('Upgrading configuration file.'))
223                        brokenwarning = True
224                    config_instance.add_section(section)
225                    configuration_values[section] = {}
226                    for keyset in configdict[section]:
227                        key, val = keyset
228                        self.update(section, key, val)
229                        configuration_values[section][key] = val
230                else:
231                    # Check if all the key:vals are in the section
232                    for keyset in configdict[section]:
233                        key, val = keyset
234                        if not configuration_values[section].has_key(key):
235                            if not console_verbosity['quiet'] and not brokenwarning:
236                                print_w(_('Detected old or broken configuration file. Fixing'))
237                            self.update(section, key, val)
238                            configuration_values[section][key] = val
239                            brokenwarning = True
240        return configuration_values
241
242    def save(self, valuesdict):
243        """
244        Saves a dictionary containing the configuration
245
246        @type valuesdict: dict
247        @param valuesdict: Dictionary of configuration
248        """
249
250        # Unpack the dict into section, option, value
251        for section in valuesdict.keys():
252            for key, value in valuesdict[section].items():
253                config_instance.set(section, key, value)
254
255        # Save
256        try:
257            config_instance.write(open(self.config_file, 'w'))
258            if console_verbosity['normal']: print_m(_('Saving configuration')) 
259        except:         
260            if not console_verbosity['quiet']: print_e(_('Could not write configuration file %s' % (self.config_file)))
261            if console_verbosity['debug']: traceback.print_exc()
262
263    def update(self, section, key, value):
264        """
265        Update a specific key's value
266
267        @type section: str
268        @param section: String of the section of the key to update
269        @type key: str
270        @param key: String of the key to update
271        @type value: str/int/bool
272        @param value: Value of the key to update
273        """     
274
275        config_instance.set(section, key, value)
276
277        try:
278            config_instance.write(open(self.config_file, 'w'))
279            if console_verbosity['debug']: print_m(_("Updating configuration key '%(key)s' to '%(value)s'") % {'key': key, 'value': value})
280        except:
281            if not console_verbosity['quiet']: print_e(_('Could not write configuration file %s' % (self.config_file)))
282            if console_verbosity['debug']: traceback.print_exc()
283
284    def create(self, path):
285        """
286        Create a configuration file from default values
287
288        @type path: str
289        @param path: Path to the configuration file
290        """
291
292        if console_verbosity['normal']: print_m(_('Creating default configuration'))
293
294        # Set default sections and options
295        for configdict in self.default_options:
296            for section in configdict:
297                config_instance.add_section(section)
298                for keyset in configdict[section]:
299                    key, val = keyset
300                    config_instance.set(section, key, val)
301
302        if not (os.path.exists(os.path.dirname(path))):
303            shutil.os.mkdir(os.path.dirname(path))
304
305        try:
306            config_instance.write(open(path, 'w'))
307        except:
308            if not console_verbosity['quiet']: print_e(_('Could not write configuration file %s' % (path)))
309            if console_verbosity['debug']: traceback.print_exc()
310
311        self.config_file = path
312
Note: See TracBrowser for help on using the repository browser.