""" 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