""" Config.py
Module for Straw's configuration settings.
"""
__copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
__license__ = """ GNU General Public License
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA. """
import cPickle, os, traceback
from urlparse import urlparse
from error import log, logtb, logparam
import pygtk
pygtk.require('2.0')
import gconf
import Event
import LookupManager
GCONF_STRAW_ROOT = "/apps/straw"
OPTION_LAST_POLL = "/general/last_poll"
OPTION_ITEMS_STORED = "/general/number_of_items_stored"
OPTION_ITEM_ORDER = "/general/item_order_newest"
OPTION_BROWSER_CMD = "/general/browser_cmd"
OPTION_WINDOW_SIZE_W = "/ui/window_width"
OPTION_WINDOW_SIZE_H = "/ui/window_height"
OPTION_MAIN_PANE_POS = "/ui/main_pane_position"
OPTION_SUB_PANE_POS = "/ui/sub_pane_position"
OPTION_WINDOW_MAX = "/ui/window_maximized"
OPTION_MAGNIFICATION = "/ui/text_magnification"
OPTION_OFFLINE = "/general/offline"
OPTION_POLL_FREQUENCY = "/general/poll_frequency"
OPTION_FEED_ID_SEQ = "feed_id_seq"
OPTION_FEEDS = "feeds"
OPTION_CATEGORIES = "categories"
def ensure_directory(strawdir):
if os.path.exists(strawdir):
if not os.path.isdir(strawdir):
raise Exception
return 0
os.mkdir(strawdir)
return 1
#############
# Config persistence classes
class ConfigPicklePersistence:
def __init__(self, filename):
self._config_file = filename
self._dict = None
def save_option(self, option, value):
if self._dict is None:
self._initialize_dict()
temp_config_file = self._config_file + ".working"
pickle_file = open(temp_config_file, "w")
self._dict[option] = value
cPickle.dump(self._dict, pickle_file, True)
pickle_file.close()
os.rename(temp_config_file, self._config_file)
return
def load_option(self, option):
if self._dict is None:
self._initialize_dict()
return self._dict.get(option, None)
def _initialize_dict(self):
if os.path.exists(self._config_file):
pickle_file = open(self._config_file, "r")
self._dict = cPickle.load(pickle_file)
pickle_file.close()
else:
self._dict = {}
class ConfigGConfPersistence:
SAVERS = {OPTION_LAST_POLL: 'int',
OPTION_ITEMS_STORED: 'int',
OPTION_ITEM_ORDER: 'bool',
OPTION_BROWSER_CMD: 'string',
OPTION_WINDOW_SIZE_W: 'int',
OPTION_WINDOW_SIZE_H: 'int',
OPTION_MAIN_PANE_POS: 'int',
OPTION_SUB_PANE_POS: 'int',
OPTION_OFFLINE: 'bool',
OPTION_WINDOW_MAX: 'bool',
OPTION_MAGNIFICATION: 'float',
OPTION_POLL_FREQUENCY: 'int'}
def __init__(self, client):
self.client = client
def save_option(self, option, value):
getattr(self.client, 'set_' + self.SAVERS[option])(
GCONF_STRAW_ROOT + option, value)
def load_option(self, option):
return getattr(self.client, 'get_' + self.SAVERS[option])(
GCONF_STRAW_ROOT + option)
class ConfigPersistence:
def __init__(self, *backends):
self.backends = backends
def save_option(self, option, value):
for b in self.backends:
if option in b[1]:
b[0].save_option(option, value)
def load_option(self, option):
for b in self.backends:
if option in b[1]:
return b[0].load_option(option)
################
# Proxy config
class BaseProxyConfig(object):
def __init__(self):
self._config_working = False
@property
def config_working(self):
return self._config_working
@apply
def host():
doc = ""
def fget(self):
return self._host
def fset(self, host):
self._host = host
self._ip = None
return property(**locals())
@apply
def ip():
doc = ""
def fget(self):
return self._ip
def fset(self, ip):
assert self._ip is None, "Overwriting proxy IP not allowed!"
if ip is None:
self.use = 0
self._ip = ip
return property(**locals())
@property
def is_waiting(self):
return self.use and not self._ip
def _ip_lookup_callback(self, host, ip, data):
self.ip = ip
def lookup_host(self):
if self._host and not self._ip:
LookupManager.get_instance().lookup(
self._host, self._ip_lookup_callback)
class GconfProxyConfig(BaseProxyConfig):
"""Encapsulate proxy use and location information (host, port, ip),
gconf reading and name lookup logic"""
GCONF_PROXY_ROOT = "/system/proxy"
GCONF_PROXY_MODE = GCONF_PROXY_ROOT + "/mode"
GCONF_HTTP_PROXY_ROOT = "/system/http_proxy"
GCONF_HTTP_PROXY_HOST = GCONF_HTTP_PROXY_ROOT + "/host"
GCONF_HTTP_PROXY_PORT = GCONF_HTTP_PROXY_ROOT + "/port"
GCONF_HTTP_PROXY_USE_AUTHENTICATION = GCONF_HTTP_PROXY_ROOT + "/use_authentication"
GCONF_HTTP_PROXY_USER = GCONF_HTTP_PROXY_ROOT + "/authentication_user"
GCONF_HTTP_PROXY_PASSWORD = GCONF_HTTP_PROXY_ROOT + "/authentication_password"
def __init__(self):
BaseProxyConfig.__init__(self)
client = gconf.client_get_default()
self._config_working = client.dir_exists(self.GCONF_PROXY_ROOT) and client.dir_exists(self.GCONF_HTTP_PROXY_ROOT)
if not self._config_working:
return
client.add_dir(self.GCONF_PROXY_ROOT, gconf.CLIENT_PRELOAD_RECURSIVE)
client.add_dir(self.GCONF_HTTP_PROXY_ROOT, gconf.CLIENT_PRELOAD_RECURSIVE)
client.notify_add(self.GCONF_PROXY_MODE, self.proxy_mode_changed)
client.notify_add(self.GCONF_HTTP_PROXY_HOST, self.proxy_host_changed)
client.notify_add(self.GCONF_HTTP_PROXY_PORT, self.proxy_port_changed)
client.notify_add(self.GCONF_HTTP_PROXY_USE_AUTHENTICATION,
self.proxy_auth_changed)
client.notify_add(self.GCONF_HTTP_PROXY_USER, self.proxy_auth_changed)
client.notify_add(self.GCONF_HTTP_PROXY_PASSWORD, self.proxy_auth_changed)
self._ip = None
pm = client.get_string(self.GCONF_PROXY_MODE)
self.use = pm is not None and pm != "none"
self.host = client.get_string(self.GCONF_HTTP_PROXY_HOST)
self.port = client.get_int(self.GCONF_HTTP_PROXY_PORT)
self.use_authentication = client.get_bool(
self.GCONF_HTTP_PROXY_USE_AUTHENTICATION)
self.user = client.get_string(self.GCONF_HTTP_PROXY_USER)
self.password = client.get_string(self.GCONF_HTTP_PROXY_PASSWORD)
# here be gconf logic
def proxy_mode_changed(self, client, notify_id, entry, *args):
value = entry.value.get_string()
self.use = value is not None and value != "none"
def proxy_host_changed(self, client, notify_id, entry, *args):
value = entry.value.get_string()
if value is not None and len(value):
self.host = value
else:
self.use = 0
def proxy_port_changed(self, client, notify_id, entry, *args):
value = entry.value.get_int()
if value is not None and value > 0:
self.port = value
else:
self.use = 0
def proxy_auth_changed(self, client, notify_id, entry, *args):
value = entry.value
if entry.key == self.GCONF_HTTP_PROXY_USE_AUTHENTICATION:
if not value:
self.use_authentication = 0
else:
self.use_authentication = 1
elif entry.key == self.GCONF_HTTP_PROXY_USER:
self.user = value
elif entry.key == self.GCONF_HTTP_PROXY_PASSWORD:
self.password = value
class EnvironmentProxyConfig(BaseProxyConfig):
"""Encapsulate proxy use and location information, environment reading"""
def __init__(self):
BaseProxyConfig.__init__(self)
try:
result = self._read_env()
except:
result = 0
self._config_working = result
self.use = self._config_working
def _read_env(self):
proxy = os.getenv("http_proxy")
if not proxy:
return 0
pparts = urlparse(proxy)
proto = pparts[0]
host = pparts[1]
if proto != "http" or not len(host):
return 0
hparts = host.split('@')
auth = self.user = self.password = self.use_authentication = None
if len(hparts) > 2:
return 0
if len(hparts) == 2:
auth, host = hparts
if auth:
aparts = auth.split(':')
if len(aparts) != 2:
return 0
self.user, self.password = aparts
self.use_authentication = 1
self.host = host
self.port = 80
hparts = host.split(':')
if len(hparts) > 2:
return 0
if len(hparts) == 2:
self.host = hparts[0]
self.port = int(hparts[1])
return 1
class NullProxyConfig(BaseProxyConfig):
def __init__(self):
BaseProxyConfig.__init__(self)
self.use = 0
self._config_working = 0
self._host = None
self._ip = None
self.port = None
self.user = None
self.password = None
self.use_authentication = None
###################
# The main man
class Config(object, Event.SignalEmitter):
_straw_dir = os.path.join(os.getenv("HOME"), ".straw")
_straw_config_file = os.path.join(_straw_dir, "config")
def __init__(self, persistence):
Event.SignalEmitter.__init__(self)
self.persistence = persistence
self.initialize_slots(Event.ItemOrderChangedSignal,
Event.OfflineModeChangedSignal,
Event.PollFrequencyChangedSignal,
Event.NumberOfItemsStoredChangedSignal)
self._feed_id_seq = 0
self._poll_freq = 1800
self._last_poll = 0
self._browser_cmd = ''
self._items_stored = 30
self._item_order = False
self._main_window_size = (640,480)
self._main_pane_position = 100
self._sub_pane_position = 100
self.first_time = None
self._offline = True
self._window_maximized = False
self._use_threads = True
self._reload_css = False
self._no_etags = False
def initialize_proxy(self):
pcchoices = (GconfProxyConfig, EnvironmentProxyConfig, NullProxyConfig)
for p in pcchoices:
self._proxy_config = p()
if self._proxy_config.config_working:
break
def initialize_options(self):
# Call this after calling Config's constructor
self.first_time = ensure_directory(self._straw_dir)
if os.path.exists(self.straw_config_file):
self._feed_id_seq = self.persistence.load_option(OPTION_FEED_ID_SEQ)
self._poll_freq = self.persistence.load_option(OPTION_POLL_FREQUENCY)
self._last_poll = self.persistence.load_option(OPTION_LAST_POLL)
self._items_stored = self.persistence.load_option(OPTION_ITEMS_STORED)
self._browser_cmd = self.persistence.load_option(OPTION_BROWSER_CMD)
self._item_order = self.persistence.load_option(OPTION_ITEM_ORDER)
width = self.persistence.load_option(OPTION_WINDOW_SIZE_W)
height = self.persistence.load_option(OPTION_WINDOW_SIZE_H)
if width <= 0:
width = 640
if height <= 0:
height = 480
self._main_window_size = (width, height)
self._main_pane_position = self.persistence.load_option(
OPTION_MAIN_PANE_POS)
self._sub_pane_position = self.persistence.load_option(
OPTION_SUB_PANE_POS)
self._window_maximized = self.persistence.load_option(OPTION_WINDOW_MAX)
self._text_magnification = self.persistence.load_option(OPTION_MAGNIFICATION)
self._offline = self.persistence.load_option(OPTION_OFFLINE)
@property
def straw_dir(self):
return self._straw_dir
@property
def straw_config_file(self):
return self._straw_config_file
@property
def proxy_config(self):
return self._proxy_config
@apply
def feeds():
doc = "Marshalled feed data"
def fget(self):
return self.persistence.load_option(OPTION_FEEDS)
def fset(self, feeddata):
self.persistence.save_option(
OPTION_FEEDS, feeddata)
return property(**locals())
@apply
def categories():
doc = ""
def fget(self):
return self.persistence.load_option(OPTION_CATEGORIES)
def fset(self, categorydata):
self.persistence.save_option(
OPTION_CATEGORIES, categorydata)
return property(**locals())
@apply
def poll_frequency():
doc = "Polling frequency"
def fget(self):
return self._poll_freq
def fset(self, poll_frequency):
if self._poll_freq != poll_frequency:
self._poll_freq = poll_frequency
self.persistence.save_option(OPTION_POLL_FREQUENCY, poll_frequency)
self.emit_signal(Event.PollFrequencyChangedSignal(self, poll_frequency))
return property(**locals())
@apply
def last_poll():
doc = "last poll"
def fget(self):
return self._last_poll
def fset(self, last_poll):
if self._last_poll != last_poll:
self._last_poll = last_poll
self.persistence.save_option(OPTION_LAST_POLL, last_poll)
return property(**locals())
@apply
def browser_cmd():
doc = "The browser to use"
def fget(self):
return self._browser_cmd
def fset(self, browser_cmd):
if self._browser_cmd != browser_cmd:
self._browser_cmd = browser_cmd
self.persistence.save_option(OPTION_BROWSER_CMD, browser_cmd)
return property(**locals())
@apply
def number_of_items_stored():
doc = "Number of items to store per feed"
def fget(self):
return self._items_stored
def fset(self, num=30):
if self._items_stored != num:
self._items_stored = num
self.persistence.save_option(OPTION_ITEMS_STORED, num)
self.emit_signal(Event.NumberOfItemsStoredChangedSignal(self))
return property(**locals())
@apply
def item_order():
doc = "Ordering of Items"
def fget(self):
return self._item_order
def fset(self, order):
if self._item_order != order:
self._item_order = order
self.persistence.save_option(OPTION_ITEM_ORDER, order)
self.emit_signal(Event.ItemOrderChangedSignal(self))
return property(**locals())
@apply
def feed_id_seq():
doc = ""
def fget(self):
return self._feed_id_seq
def fset(self, id):
self._feed_id_seq = id
self.persistence.save_option(OPTION_FEED_ID_SEQ, id)
return property(**locals())
def next_feed_id_seq(self):
self.feed_id_seq += 1
return self._feed_id_seq
@apply
def main_window_size():
doc = ""
def fget(self):
return self._main_window_size
def fset(self, size):
if self._main_window_size != size:
self._main_window_size = size
self.persistence.save_option(OPTION_WINDOW_SIZE_W, size[0])
self.persistence.save_option(OPTION_WINDOW_SIZE_H, size[1])
return property(**locals())
@apply
def offline():
doc = ""
def fget(self):
return self._offline
def fset(self, mode):
if self._offline != mode:
self._offline = mode
self.persistence.save_option(OPTION_OFFLINE, mode)
self.emit_signal(Event.OfflineModeChangedSignal(self))
return property(**locals())
@apply
def window_maximized():
doc = ""
def fget(self):
return self._window_maximized
def fset(self, state):
if self._window_maximized is not state:
self._window_maximized = state
self.persistence.save_option(OPTION_WINDOW_MAX, state)
return property(**locals())
@apply
def main_pane_position():
doc = ""
def fget(self):
return self._main_pane_position
def fset(self, position):
if self._main_pane_position != position:
self._main_pane_position = position
self.persistence.save_option(OPTION_MAIN_PANE_POS, position)
return property(**locals())
@apply
def sub_pane_position():
doc = ""
def fget(self):
return self._sub_pane_position
def fset(self, position):
if self._sub_pane_position != position:
self._sub_pane_position = position
self.persistence.save_option(OPTION_SUB_PANE_POS, position)
return property(**locals())
@apply
def text_magnification():
doc = "sets the amount of magnification in the item view"
def fget(self):
return self._text_magnification
def fset(self, tm):
if self._text_magnification is not tm:
self._text_magnification = tm
self.persistence.save_option(OPTION_MAGNIFICATION, tm)
return property(**locals())
@apply
def use_threads():
doc = ""
def fget(self):
return self._use_threads
def fset(self, threads):
self._use_threads = threads
return property(**locals())
@apply
def reload_css():
doc = ""
def fget(self):
return self._reload_css
def fset(self, reload_css):
self._reload_css = reload_css
return property(**locals())
@apply
def no_etags():
doc = ""
def fget(self):
return self._no_etags
def fset(self, no_etags):
self._no_etags = no_etags
return property(**locals())
def convert_if_necessary(config):
if not os.path.exists(config.straw_config_file):
return
try:
f = open(config.straw_config_file, "r")
cf = cPickle.load(f)
feeds = cf.get('feeds', None)
if feeds and feeds[0].get('_n_items_unread', None) is None:
def _convert_feeds(feeds, parent):
import ItemStore
itemstore = ItemStore.get_instance(config.straw_dir)
for feed in feeds:
if isinstance(feed, list):
_convert_feeds(feed[1:], parent)
else:
stored = feed.get('_items_stored', -1)
if stored > -1:
cutoff = stored
else:
cutoff = config.number_of_items_stored
feed['_n_items_unread'] = itemstore.get_number_of_unread(feed['_id'], cutoff)
return feeds
config.feeds = _convert_feeds(feeds, None)
if cf.has_key('poll_frequency'):
config.poll_frequency = cf.get('poll_frequency')
config.number_of_items_stored = cf.get('number_of_items_stored')
config.item_order = cf.get('item_order')
config.main_window_size = cf.get('main_window_size')
config.main_pane_position = cf.get('main_pane_position')
config.sub_pane_position = cf.get('sub_pane_position')
config.offline = cf.get('offline')
del cf['poll_frequency']
del cf['number_of_items_stored']
del cf['item_order']
del cf['main_window_size']
del cf['main_pane_position']
del cf['sub_pane_position']
del cf['offline']
f.close()
f = open(config.straw_config_file, "w")
f.seek(0)
f.truncate()
cPickle.dump(cf, f, True)
finally:
f.close()
return
def create_gconf_persistence():
client = gconf.client_get_default()
client.add_dir(GCONF_STRAW_ROOT, gconf.CLIENT_PRELOAD_ONELEVEL)
return ConfigGConfPersistence(client)
def create_pickle_persistence():
return ConfigPicklePersistence(Config._straw_config_file)
def create_instance():
# Add your GConf key here as well!
cp = ConfigPersistence(
(create_gconf_persistence(),
(OPTION_LAST_POLL, OPTION_ITEMS_STORED, OPTION_ITEM_ORDER, OPTION_BROWSER_CMD,
OPTION_WINDOW_SIZE_W, OPTION_WINDOW_SIZE_H,
OPTION_MAIN_PANE_POS, OPTION_SUB_PANE_POS, OPTION_OFFLINE,
OPTION_MAGNIFICATION,
OPTION_POLL_FREQUENCY, OPTION_WINDOW_MAX)),
(create_pickle_persistence(),
(OPTION_FEED_ID_SEQ, OPTION_FEEDS, OPTION_CATEGORIES)))
config = Config(cp)
config.initialize_proxy()
config.initialize_options()
convert_if_necessary(config)
return config
config_instance = None
def get_instance():
global config_instance
if config_instance is None:
config_instance = create_instance()
return config_instance
syntax highlighted by Code2HTML, v. 0.9.1