source: tags/0.2.2/config.py @ 329

Revision 329, 11.0 KB checked in by marc, 3 years ago (diff)

test new save path

  • Property svn:keywords set to Id Rev
Line 
1#! /usr/bin/env python
2# -*- coding: utf8 -*-
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 2 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 parser and engine """
24
25# It works by the core initiating the main instance, and the
26# modules accessing the global values variables set up by the initation.
27
28import ConfigParser, os, sys, shutil, traceback
29
30# Set up instance
31config = ConfigParser.ConfigParser()
32
33# Set up global variables (itakaglobals)
34
35#: Configuration file
36local_config = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "itaka.conf")
37
38#: Version
39version = "0.2.2"
40#: Revision
41revision = "$Rev$"
42
43#: System
44system = os.name
45
46#: Platform
47platform = None
48if (sys.platform.startswith("darwin")): platform = "darwin"
49
50#: Images directory
51image_dir = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "share/images/")
52#: To be changed on install to specify where the installed files actually are
53prefix = "/usr/local/share/itaka/images/"
54if os.path.exists(prefix):
55    image_dir = prefix
56
57# See if our images are there before starting
58if not os.path.exists(image_dir):
59    print "[*] ERROR: Could not find images directory '%s'" % (image_dir)
60    sys.exit(1)
61
62#: Save path for screenshots (system-specific specified later on)
63try:
64    save_path = os.getcwd()
65except:
66    print "[*] WARNING: Could not get current directory"
67
68# Try APPDATA on Windows or $HOME on POSIX
69if (system == 'nt'):
70    if os.environ.get('APPDATA'):
71                save_path = os.path.join(os.environ.get('APPDATA'), 'itaka')
72    elif os.environ.get('HOME'):
73                save_path = os.path.join(os.environ.get('HOME'), 'itaka')
74else:
75    if os.environ.get('HOME'):
76        save_path = os.path.join(os.environ.get('HOME'), '.itaka')
77
78#: Availability of libnotify
79notifyavailable = False
80if system == "posix" and platform != "darwin":
81    try:
82        import pynotify
83        notifyavailable = True
84
85        if not pynotify.init("Itaka"):
86            print "[*] WARNING: Pynotify module is failing, disabling notifications"
87            notifyavailable = False
88    except ImportError:
89        print "[*] WARNING: Pynotify module is missing, disabling notifications"
90        notifyavailable = False
91
92#: Console output setting
93# 'normal' is for all normal operation mesages and warnings (not including errors)
94# 'debug' is for all messages through self.console.debug
95# 'quiet' is to quiet all errors and warnings. (totally quiet is in conjunction with 'normal')
96output = {'normal': False, 'debug': False, 'quiet': False}
97
98#: User's configuration values
99values = {}
100
101#: Default HTML header.
102# Putting <meta http-equiv="Refresh" content="5; url=http://localhost:8000"> is very useful for debugging
103headhtml = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
104<html>
105<head>
106<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
107<title>Itaka</title>
108</head>
109<body>
110<div id="main">
111'''
112
113#: Default HTML footer.
114footerhtml = '''
115</div>
116</body>
117</html>'''
118
119class ConfigParser:
120    """
121    Itaka configuration engine.
122    """
123
124    def __init__(self, arguments=1):
125        """
126        Configuration engine constructor. It also handles whether the L{output} setting is set to print everything to the console.
127
128        @type arguments: tuple
129        @param arguments: A tuple of sys.argv
130        """
131        if len(arguments) > 1 and arguments[-1] in ("--debug", "-d"):
132            global output
133            output = {'normal': True, 'debug': True, 'quiet': False}
134            print "[*] Initializing in debug mode"
135
136        #: Default configuration sections and values
137        self.defaultoptions = (
138                {'server': (('port', 8000), ('authentication', False), ('username', 'user'), ('password', 'password'), ('notify', notifyavailable))},
139                {'screenshot': (('format', 'jpeg'), ('quality', 30), ('path', save_path), ('currentwindow', False), ('scale', False), ('scalepercent', 100))},
140                {'html': (('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.">'), ('authfailure', '<p><strong>Sorry, but you cannot access this resource without authorization.</strong></p>'))}
141                )
142
143    def load(self):
144        """
145        Set up and load configuration
146
147        @rtype: dict
148        @return: Dictionary of configuration values.
149        """
150
151        self.configfile = None
152
153        # Check routine
154        if system in ("posix"):
155            if not (os.path.exists(os.path.join(os.environ['HOME'], ".itaka/itaka.conf"))):
156                self.create(os.path.join(os.environ['HOME'], ".itaka/itaka.conf"))
157            else:
158                self.configfile = os.path.join(os.environ['HOME'], ".itaka/itaka.conf")
159        elif (system == "nt"):
160            if not (os.path.exists(os.path.join(os.environ['APPDATA'], "itaka/itaka.ini"))):
161                self.create(os.path.join(os.environ['APPDATA'], "itaka/itaka.ini"))
162            else:
163                self.configfile = os.path.join(os.environ['APPDATA'], "itaka/itaka.ini")
164        else:
165            # Generic system/paths (using local)       
166            if (os.path.exists(local_config)):
167                self.configfile = local_config
168            else:       
169                self.create(local_config)
170        # Read and assign values from the configuration file
171        try:
172            config.read(self.configfile)
173            if output['normal']: print "[*] Read configuration (%s)" % (self.configfile)
174
175        except:
176            if output['normal']: print "[*] ERROR: Could not read configuration file (%s)" % (self.configfile)
177            if output['debug']: traceback.print_exc()
178
179        """ Retrieve values and return them as a dict """
180        global values
181        values = {}
182        # Get values as a dict and return it
183        for section in config.sections():
184            values[section] = dict(config.items(section))
185            # Convert 'False' and 'True' into booleans, and numbers into ints
186            # Add config options that are not there
187            for option, value in values[section].iteritems():
188                if value.strip() == "True":
189                    values[section][option] = True
190                elif value.strip() == "False":
191                    values[section][option] = False
192                elif value.isdigit():
193                    values[section][option] = int(value)
194
195        # Compare it to our default configuration set, to see if there is anything missing
196        # This is useful for updates, and corrupted files.
197        # NOTE: The setting of values[section][key] here is purely pragmatical, so we
198        # dont have to reload
199        brokenwarning = False
200        for configdict in self.defaultoptions:
201            for section in configdict:
202                if not values.has_key(section):
203                    if not output['quiet'] and not brokenwarning:
204                        print '[*] WARNING: Detected old or broken configuration file. Fixing'
205                        brokenwarning = True
206                    config.add_section(section)
207                    values[section] = {}
208                    for keyset in configdict[section]:
209                        key, val = keyset
210                        self.update(section, key, val)
211                        values[section][key] = val
212                else:
213                    # Check if all the key:vals are in the section
214                    for keyset in configdict[section]:
215                        key, val = keyset
216                        if not values[section].has_key(key):
217                            if not output['quiet'] and not brokenwarning: print "[*] WARNING: Detected old or broken configuration file. Fixing"
218                            self.update(section, key, val)
219                            values[section][key] = val
220                            brokenwarning = True
221        return values
222
223    def save(self, valuesdict):
224        """
225        Saves a dictionary containing the configuration.
226
227        @type valuesdict: dict
228        @param valuesdict: Dictionary of configuration.
229        """
230
231        # Unpack the dict into section, option, value
232        for section in valuesdict.keys():
233            for key, value in valuesdict[section].items():
234                config.set(section, key, value)
235
236        # Save
237        try:
238            config.write(open(self.configfile, 'w'))
239            if output['normal']: print "[*] Saving configuration... "   
240        except:         
241            if not output['quiet']: print "[*] ERROR: Could not write configuration file %s" % (self.configfile)
242            if output['debug']: traceback.print_exc()
243
244    def update(self, section, key, value):
245        """
246        Update a specific key's value.
247       
248        @type section: str
249        @param section: String of the section of the key to update.
250        @type key: str
251        @param key: String of the key to update.
252        @type value: str/int/bool
253        @param value: Value of the key to update.
254        """     
255       
256        config.set(section, key, value)
257       
258        try:
259            config.write(open(self.configfile, 'w'))
260            if output['debug']: print "[*] Updating configuration key %s to %s" % (key, value) 
261        except:
262            if not output['quiet']: print "[*] ERROR: Could not write configuration file %s" % (self.configfile)
263            if output['debug']: traceback.print_exc()
264
265    def create(self, path):
266        """
267        Create a configuration file from default values.
268       
269        @type path: str
270        @param path: Path to the configuration file.
271        """
272       
273        if output['normal']: print "[*] Creating default configuration..."
274
275        # Set default sections and options
276        for configdict in self.defaultoptions:
277            for section in configdict:
278                config.add_section(section)
279                for keyset in configdict[section]:
280                    key, val = keyset
281                    config.set(section, key, val)
282
283        # Check if the directory exists, if not create it
284        # and write the config file with its variables
285        if not (os.path.exists(os.path.dirname(path))):
286            shutil.os.mkdir(os.path.dirname(path))
287
288        try:
289            config.write(open(path, 'w'))
290        except:
291            if not output['quiet']: print "[*] ERROR: Could not write configuration file %s" % (path)
292            if output['debug']: traceback.print_exc()
293
294        self.configfile = path         
Note: See TracBrowser for help on using the repository browser.