#!/usr/bin/python
# -*- coding: utf-8 -*-
# vital_linux_tweaks.v.2.0.py
# 2007 Nov 09 . ccr
u"""*vital_linux_tweaks.py* updates your Linux configuration.
Copyright © 2007 Charles Curtis Rhode
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.
A copy of the GNU General Public License is available as
/usr/share/common-licenses/GPL in the Debian GNU/Linux
distribution or on the World Wide Web at
http://www.gnu.org/copyleft/gpl.html. You can also obtain it by
writing to the Free Software Foundation, Inc., 51 Franklin St,
Fifth Floor, Boston, MA 02110-1301 USA
Charles Curtis Rhode, CRhode@LacusVeris,
1518 N 3rd, Sheboygan, WI 53081
This script changes the look-and-feel of bash, GNOME, Firefox, et al.
It demonstrates how messy life in a GUI world really is.
Rather than configure myriads upon myriads of options, settings, and
properties via the GUI interfaces of various applications, this script
tweaks their Gnome config entries and their text-based configuration
files, which are scattered throughout the root and user directories.
This script is run from the command line. It solicits no input from
the console.
Obviously no single script can set up a Gnome/Linux desktop
environment ideally for all users or even for any two users. This
script sets up my environment for me. You may be able to use parts of
it.
Obviously this script is good only for specific versions of software
and is dangerous to run unless you are absolutely certain what levels
are installed. It was developed for Debian Gnu/Linux 4.0 r1 _Etch_
i386, but v.2.0 is for Debian Gnu/Linux 5.0 r1 _Lenny_.
o It makes look-and-feel portable between user accounts and between
computers running identical software-revision levels.
o It makes recovery of look-and-feel semi-reliable and quick after
reloads.
o It is more-or-less stateless. It can be run and yet re-run when in
doubt about the current state of affairs.
o By showing what changes are made where, it documents those changes.
o It is ad hoc. No-body and no-thing generates a unified log of the
changes you make through the GUI interfaces. Instead, you have to
scribble each change down, research the impact of it, and replicate
its effect on sundry config files.
One may ask whence the need for yet another configuration tool like
*vital_linux_tweaks.py* when one might just as well use *debconf*.
That is a very good question! The answer has much to do with
remembering which things you changed as opposed to those you didn't.
Also, it has to do with documenting in one place what your changes
really do, which this script does.
Users should read the code below the comment, \"Mainline begins
here.\" Those who understand the why's and wherefore's of this script
are encouraged to use it and adapt it to their own needs. To
reiterate: This script employs DANGEROUS techniques. Please use it
with an informed level of care.
Particularly, change the list of USERS, the list of HOSTS, and the
global constants, below.
Thank you for trying *vital_linux_tweaks.py*.
-- ccr
\"I hate to advocate drugs, alcohol, violence or insanity to
anyone, but they've always worked for me.\"
-- Hunter S. Thompson
PS: There are aspects of configuration beyond those that can be
addressed by this script.
o The setups for certain packages ought to be brought forward (ported)
from an old PC rather than be tweaked or overlaid. Copying all the
files in a user's \"home\" directory from the previous system takes
care of this mostly. Pay special attention, though, to any packages
that run at root authority. *fetchmail* may be the prime example.
Its config file is in the *root* directory.
o Note: *HylaFAX* emails monthly reports (and notice of FAXes received)
to \"faxmaster.\" *exim4* is typically configured to route all outbound
mail through your ISP's SmartHost without attempting any local
delivery. Thus, you need to configure at least an alias for \"faxmaster\"
inbound eMail at your ISP.
Here are the packages I use and assume to be installed:
-------------
Configuration
-------------
o printconf
o synaptic
--------------
Communications
--------------
o curl
o fetchmail
o flashplugin-nonfree
o gnome-ppp
o gnubiff
o ntp
o pan
o pcmcia-cs
o procmail
o rblcheck
o rsync
o ssh
o update-mgr
o update-notifier
o wireless-tools
o wpasupplicant
-----------
Development
-----------
o automake
o binutils
o dpkg-dev
o dselect
o firebug
o g++
o linux-kernel-headers
o make
o mtools
o python-gtk-dev
------
Editor
------
o emacs
o mc
o python-mode
o trash-cli
-----------
Look & Feel
-----------
o boinc_client
o boinc_manager
o clamav
o devilspie
o esound
o esound-clients
o fam
o gnome-audio
o libesd-alsa0
o rhythmbox
o ttf-bitstream-vera
o ttf-opensymbol
------------
Productivity
------------
o dcraw
o exiv2
o gimp
o gnomebaker
o gnucash
o hylafax
o librsvg2-bin
o netpbm
o openoffice.org
o xsane
-----------
Third Party
-----------
o elementtree from http://effbot.org/downloads/#elementtree
o iniparse from http://code.google.com/p/iniparse/
o metar from http://pypi.python.org/pypi/metar/1.3.0
o prefbar from http://prefbar.mozdev.org
"""
# 2009 Feb 25 . ccr . Revised for Debian 5.0 (Lenny).
# 2007 Jul 22 . ccr . Revert to dialup.
from __future__ import division
import sys
import os
import stat
import codecs
import time
import datetime
import re
import shlex
import subprocess
import pwd
import gconf
import StringIO
import urllib2
import base64
try:
import iniparse
IniFileParser = iniparse # from http://code.google.com/p/iniparse/
# *iniparse* is *not* part of the standard Python distribution.
# However, it preserves the comments and section ordering in
# config files, which it reads and writes. The *ConfigParser* in
# the standard distribution does not. Many Linux config files
# have embedded commentary that should be conserved against the
# probability of having to do maintenance on them.
except ImportError:
import ConfigParser
IniFileParser = ConfigParser
PY_VER = sys.version
if PY_VER[:3] in ['2.5']:
import xml.etree.ElementTree
ElementTree = xml.etree.ElementTree
else:
import elementtree.ElementTree # from http://effbot.org/zone/element-index.htm
ElementTree = elementtree.ElementTree
ZERO = 0
SPACE = ' '
NULL = ''
NUL = '\x00'
NA = -1
def when(*circumstances):
"""Protects invocation of each mainline component.
This function returns true when the situation constant (below)
covers any of the instant circumstances the function was called
with. You change the situation (here) to allow execution of
different selections of mainline components.
"""
situation = [
# 'obsolete',
# 'long',
# 'configuration',
# 'look_and_feel',
# 'communications',
# 'news',
# 'mail',
# 'browser',
# 'editor',
# 'printing',
# 'fax',
# 'sound',
# 'personal',
# 'desktop_icons',
'development',
]
quals = set(situation).intersection(circumstances)
result = bool(quals)
return result
class User(object):
"""Record of ID for each user.
o *name* is the username.
name='xxx'
name='CRhode' # for example.
o *real_name* is the human-readable name of the user for the
headers of newsgroup postings and possibly outbound eMail.
real_name='Xxx Xxx'
real_name='Chuck Rhode' # for example.
o *email* is the inbound eMail address of the user.
email='xxx@xxx.xxx'
email='CRhode@LacusVeris.com' # for example.
o *sig* is a shell command that prints a signature suitable for
appending to outbound eMail.
sig='xxx xxx'
sig='echo -e --\nChuck Rhode' # default.
o *wallpaper* is the path to a *.jpg file suitable for the
background of the user's desktop.
wallpaper='xxx.jpg'
wallpaper='/home/crhode/photo_album/background.jpg' # for example.
"""
def __init__(
self,
name,
real_name,
email,
sig=None,
wallpaper=NULL,
wallpaper_style = 'scaled',
background_colors=('#006699', '#0099CC'),
):
self.name = name
self.real_name = real_name
self.email = email
if sig is None:
self.sig = 'echo -e --\n%s' % self.real_name
else:
self.sig = sig
self.wallpaper = wallpaper
self.wallpaper_style = wallpaper_style
self.background_colors = background_colors
pwdb = pwd.getpwnam(self.name)
self.uid = pwdb.pw_uid
self.gid = pwdb.pw_gid
return
class Host(object):
""" Record of ID for each PC.
This is for initializing the */etc/hosts* file and
*/etc/network/interfaces*:
o *host_name* is the mnemonic name of the PC on the
local-area-network (LAN).
host_name='xxx'
host_name='loki' # for example
o *static_ip* is the IP-quad address of the PC on the LAN.
static_ip='255.255.255.255'
static_ip='192.168.1.20' # for example.
"""
def __init__(self, host_name, static_ip, **attribs):
self.host_name = host_name
self.static_ip = static_ip
self.attribs = attribs
self.gateway = GATEWAY
self.fqdn = '%s.%s' % (self.host_name, FQDN)
return
# -----
# You need to change these configuration global constants to reflect
# your situation, not mine:
# -----
# Your domain name:
DOMAIN = 'LacusVeris'
# This script plugs in this value wherever the domain name should
# appear in various config files -- such as that of the mail
# transfer agent (MTA).
# DOMAIN = 'xxx'
# DOMAIN = 'LacusVeris' # for example.
# The fully-qualified domain name:
FQDN = '%s.com' % DOMAIN
# This script plugs in this value wherever the fully-qualified
# domain name should appear in various config files.
# FQDN = 'xxx.xxx'
# FQDN = 'LacusVeris.com' # for example.
# The user's Web site:
WWW = 'www.%s' % FQDN
# This script plugs in this value where directed -- such as in the
# newsgroup signature block.
# WWW = 'www.xxx.xxx'
# WWW = 'www.LacusVeris.com' # for example.
# The IP addr of the Internet gateway:
GATEWAY = '192.168.1.1'
# This is the IP-quad address of a local router (if any);
# otherwise, it is the address of the Internet Service Provider's
# (the ISPs) router. It is used to initialize
# */etc/network/interfaces*.
# GATEWAY = '255.255.255.255'
# GATEWAY = '192.168.1.1' # for example.
# Your username and password for authenticating outbound mail:
SMARTHOST_USER = 'mail.%s' % FQDN
SMARTHOST_PASSWORD = '***'
# This is the ID required by your ISP to accept your outbound
# eMail. It is used in the MTA configuration.
# SMARTHOST_USER = 'mail.xxx.xxx'; SMARTHOST_PASSWORD = '***'
# SMARTHOST_USER = 'mail.LacusVeris.com'; SMARTHOST_PASSWORD = '***' # for example.
# Your username and password for FTP access to maintain your Web site:
FTP_USER = 'lacusver'
FTP_PASSWORD = '***'
# This is the ID required by your Web-hosting provider. It is used
# to configure default FTP access such as through Midnight
# Commander (*mc*).
# FTP_USER = 'xxx'; FTP_PASSWORD = '***'
# FTP_USER = 'lacusver'; FTP_PASSWORD = '***' # for example.
# IDs for the GUI users you want to enable on this PC:
USERS = [
User(
name='crhode',
real_name='Chuck Rhode',
email='CRhode@%s' % FQDN,
sig='/home/crhode/cgi-bin/WX/Sig.py /home/crhode/.rec.moto.sig "http://%s/WX/OneLiner.shtml?State=WI&County=WIC117&Zone=WIZ052&WFO=MKW&Station=KSBM"' % WWW,
wallpaper='/home/crhode/photo_album/American Monet, US 2, MT 2004-07-30 21.58.40.jpg',
),
]
# IDs for the PCs on your LAN that you want to ping mnemonically:
HOSTS = [
Host(
host_name='loki',
static_ip = '192.168.1.20',
desktop=True,
),
Host(
host_name='thor',
static_ip = '192.168.1.21',
laptop=True,
),
]
# Relative path for the "Personal Slideshow" screensaver repertoire
# folder:
SCREENSAVER_PERSONAL_SLIDE_SHOW_FRAMES = '.screensaver-personal-slideshow-frames'
# This is set for all users. That is: Each user has a similarly
# named folder in his "home" directory:
# SCREENSAVER_PERSONAL_SLIDE_SHOW_FRAMES = '.xxx'
# SCREENSAVER_PERSONAL_SLIDE_SHOW_FRAMES = 'Pictures' # Default
# MAC address for WIFI card:
WIFI_MAC = 'ff:ff:ff:ff:ff:ff'
# This is used to set a persistent device mnemonic for the WIFI
# connection.
# WIFI_MAC = 'ff:ff:ff:ff:ff:ff' # Use lowercase.
# MAC address for Ethernet card:
ETH0_MAC_DESKTOP = 'ff:ff:ff:ff:ff:ff'
# This is used to set a persistent device mnemonic for the Ethernet
# connection.
# ETH0_MAC_DESKTOP = 'ff:ff:ff:ff:ff:ff' # Use lowercase.
ETH0_MAC_LAPTOP = 'ff:ff:ff:ff:ff:ff'
# This is used to set a persistent device mnemonic for the Ethernet
# connection.
# ETH0_MAC_LAPTOP = 'ff:ff:ff:ff:ff:ff' # Use lowercase.
# FAX MODEM:
FAX_MODEM = 'ttyS1'
# This is the serial port of your FAX device.
# FAX_MODEM = 'ttyS9'
# FAX_MODEM = 'ttyS3' # for example.
# FAX Area Code:
FAX_AREA_CODE = '999'
# This is the area code of your FAX phone number.
# FAX_AREA_CODE = '999'
# FAX_AREA_CODE = '920' # for example.
# FAX Phone:
FAX_PHONE = '+1.999.999.9999'
# This is the actual telephone number you use for outbound (and
# inbound) FAXes.
# FAX_PHONE = '+1.999.999.9999'
# FAX_PHONE = '+1.920.459.9492' # for example.
# FAX ID:
# This is your company name.
FAX_ID = ''
# FAX_ID = 'xxx'
# FAX_ID = FAX_PHONE # default
ISP_PHONE = '4520513'
# This is the dial-up phone of your Internet Service Provider's
# (ISPs) MODEM bank.
# ISP_PHONE = '1.999.999.9999'
# ISP_PHONE = '4520513' # for example.
PPP_USER = '***'
PPP_PASSWORD = '***'
# This is the dialup ID required by your Internet Service Provider.
# PPP_USER = 'xxx'; PPP_PASSWORD = '***'
# PPP_USER = 'xxx'; PPP_PASSWORD = '***' # for example.
# -----
# End of configuration global constants.
# -----
# What follows are implementation details. You may skip ahead to
# Mainline.
try:
from vital_passwords import *
except ImportError:
pass
TODAY = datetime.date.today()
TODAY_STRING = TODAY.strftime('%Y %b %d')
TEMPLATE_XML_APP = """
action_type
/schemas/apps/panel/objects/action_type
lock
attached_toplevel_id
/schemas/apps/panel/objects/attached_toplevel_id
bonobo_iid
/schemas/apps/panel/objects/bonobo_iid
%(bonobo_iid)s
custom_icon
/schemas/apps/panel/objects/custom_icon
launcher_location
/schemas/apps/panel/objects/launcher_location
locked
/schemas/apps/panel/objects/locked
false
menu_path
/schemas/apps/panel/objects/menu_path
applications:/
object_type
/schemas/apps/panel/objects/object_type
bonobo-applet
panel_right_stick
/schemas/apps/panel/objects/panel_right_stick
%(right_stick)s
position
/schemas/apps/panel/objects/position
1
tooltip
/schemas/apps/panel/objects/tooltip
toplevel_id
/schemas/apps/panel/objects/toplevel_id
%(panel)s
use_custom_icon
/schemas/apps/panel/objects/use_custom_icon
false
use_menu_path
/schemas/apps/panel/objects/use_menu_path
false
"""
TEMPLATE_XML_APP_SYSTEM_MONITOR = """
action_type
/schemas/apps/panel/objects/action_type
lock
attached_toplevel_id
/schemas/apps/panel/objects/attached_toplevel_id
bonobo_iid
/schemas/apps/panel/objects/bonobo_iid
%(bonobo_iid)s
custom_icon
/schemas/apps/panel/objects/custom_icon
launcher_location
/schemas/apps/panel/objects/launcher_location
locked
/schemas/apps/panel/objects/locked
false
menu_path
/schemas/apps/panel/objects/menu_path
applications:/
object_type
/schemas/apps/panel/objects/object_type
bonobo-applet
panel_right_stick
/schemas/apps/panel/objects/panel_right_stick
%(right_stick)s
position
/schemas/apps/panel/objects/position
1
tooltip
/schemas/apps/panel/objects/tooltip
toplevel_id
/schemas/apps/panel/objects/toplevel_id
%(panel)s
use_custom_icon
/schemas/apps/panel/objects/use_custom_icon
false
use_menu_path
/schemas/apps/panel/objects/use_menu_path
false
prefs/cpuload_color0
/schemas/apps/multiload/prefs/cpuload_color0
#0072b3
prefs/cpuload_color1
/schemas/apps/multiload/prefs/cpuload_color1
#0092e6
prefs/cpuload_color2
/schemas/apps/multiload/prefs/cpuload_color2
#00a3ff
prefs/cpuload_color3
/schemas/apps/multiload/prefs/cpuload_color3
#ffffff
prefs/cpuload_color4
/schemas/apps/multiload/prefs/cpuload_color4
#000000
prefs/diskload_color0
/schemas/apps/multiload/prefs/diskload_color0
#C65000
prefs/diskload_color1
/schemas/apps/multiload/prefs/diskload_color1
#FF6700
prefs/diskload_color2
/schemas/apps/multiload/prefs/diskload_color2
#000000
prefs/loadavg_color0
/schemas/apps/multiload/prefs/loadavg_color0
#d50000
prefs/loadavg_color1
/schemas/apps/multiload/prefs/loadavg_color1
#000000
prefs/memload_color0
/schemas/apps/multiload/prefs/memload_color0
#00b35b
prefs/memload_color1
/schemas/apps/multiload/prefs/memload_color1
#00e675
prefs/memload_color2
/schemas/apps/multiload/prefs/memload_color2
#00ff82
prefs/memload_color3
/schemas/apps/multiload/prefs/memload_color3
#AAF5D0
prefs/memload_color4
/schemas/apps/multiload/prefs/memload_color4
#000000
prefs/netload_color0
/schemas/apps/multiload/prefs/netload_color0
#00b0b3
prefs/netload_color1
/schemas/apps/multiload/prefs/netload_color1
#00e2e6
prefs/netload_color2
/schemas/apps/multiload/prefs/netload_color2
#00fbff
prefs/netload_color3
/schemas/apps/multiload/prefs/netload_color3
#B7FEFF
prefs/netload_color4
/schemas/apps/multiload/prefs/netload_color4
#000000
prefs/size
/schemas/apps/multiload/prefs/size
40
prefs/speed
/schemas/apps/multiload/prefs/speed
500
prefs/swapload_color0
/schemas/apps/multiload/prefs/swapload_color0
#8b00c3
prefs/swapload_color1
/schemas/apps/multiload/prefs/swapload_color1
#000000
prefs/view_cpuload
/schemas/apps/multiload/prefs/view_cpuload
%(view_load_cpu)s
prefs/view_diskload
/schemas/apps/multiload/prefs/view_diskload
%(view_load_io)s
prefs/view_loadavg
/schemas/apps/multiload/prefs/view_loadavg
%(view_load_average)s
prefs/view_memload
/schemas/apps/multiload/prefs/view_memload
%(view_load_memory)s
prefs/view_netload
/schemas/apps/multiload/prefs/view_netload
%(view_load_network)s
prefs/view_swapload
/schemas/apps/multiload/prefs/view_swapload
%(view_load_swap)s
"""
TEMPLATE_BASH_PROMPT = r'''#!/bin/bash
# .bash_prompt.sh
EMPH_IN=""
EMPH_OUT=""
if [ "$TERM" != "dumb" ]; then
EMPH_IN="\e[34;47m"
EMPH_OUT="\e[0m"
fi
export PS1="\[$EMPH_IN\]\t \w\[$EMPH_OUT\] \\$ "
# Note the elegant bracketing of the escape sequences.
# This keeps them from being counted in the length of the prompt
# so that line wrapping works correctly.
unset EMPH_IN
unset EMPH_OUT
# fin!
'''
TEMPLATE_NETWORK_INTERFACES = r'''
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
manual eth0
#iface eth0 inet dhcp
iface eth0 inet static
address %(static_ip)s
netmask 255.255.255.0
gateway %(gateway)s
# Wireless network interface
auto wifi0
allow-hotplug wifi0
iface wifi0 inet dhcp
# Dialup interface
manual ppp0
iface ppp0 inet ppp
provider ppp0
'''
TEMPLATE_DEVILS_PIE = '''; %(init_file_name)s
(if (%(test)s (%(property_)s) "%(key)s") (%(action)s))
'''
TEMPLATE_DESKTOP_ENTRY = '''[Desktop Entry]
Encoding=UTF-8
Version=1.0
TryExec=
Icon=%(icon)s
X-GNOME-DocPath=
Name=%(desktop_name)s
Name[en_US]=%(desktop_name)s
GenericName=%(categories)s
GenericName[en_US]=%(categories)s
Comment=%(comment)s
Comment[en_US]=%(comment)s
'''
TEMPLATE_DESKTOP_LAUNCHER = TEMPLATE_DESKTOP_ENTRY + '''Type=Application
Terminal=false
Exec=%(exec_)s
'''
TEMPLATE_DESKTOP_LAUNCHER_TERM = TEMPLATE_DESKTOP_ENTRY + '''Type=Application
Terminal=true
Exec=%(exec_)s
'''
TEMPLATE_DESKTOP_LINK = TEMPLATE_DESKTOP_ENTRY + '''Type=Link
Terminal=false
URL=%(link)s
'''
TEMPLATE_PAN_SERVERS = r'''
news.aioe.org
119
15
4
2
news.excel.net
119
15
4
1
'''
TEMPLATE_PAN_ID = r'''
%(id)s
%(email)s
2
%(sig_command)s
On %%d, %%n wrote:
'''
TEMPLATE_WALLPAPER = r'''
color_shading_type
/schemas/desktop/gnome/background/color_shading_type
vertical-gradient
draw_background
/schemas/desktop/gnome/background/draw_background
true
picture_filename
/schemas/desktop/gnome/background/picture_filename
%(picture)s
picture_opacity
/schemas/desktop/gnome/background/picture_opacity
100
picture_options
/schemas/desktop/gnome/background/picture_options
%(style)s
primary_color
/schemas/desktop/gnome/background/primary_color
%(color_top)s
secondary_color
/schemas/desktop/gnome/background/secondary_color
%(color_bottom)s
'''
TEMPLATE_TERM_PROFILE_TRANSPARENT_HOLD = r'''
allow_bold
/schemas/apps/gnome-terminal/profiles/Default/allow_bold
true
background_color
/schemas/apps/gnome-terminal/profiles/Default/background_color
#FFFFDD
background_darkness
/schemas/apps/gnome-terminal/profiles/Default/background_darkness
0.27860695123672485
background_image
/schemas/apps/gnome-terminal/profiles/Default/background_image
background_type
/schemas/apps/gnome-terminal/profiles/Default/background_type
transparent
backspace_binding
/schemas/apps/gnome-terminal/profiles/Default/backspace_binding
ascii-del
custom_command
/schemas/apps/gnome-terminal/profiles/Default/custom_command
default_show_menubar
/schemas/apps/gnome-terminal/profiles/Default/default_show_menubar
true
delete_binding
/schemas/apps/gnome-terminal/profiles/Default/delete_binding
escape-sequence
exit_action
/schemas/apps/gnome-terminal/profiles/Default/exit_action
hold
font
/schemas/apps/gnome-terminal/profiles/Default/font
monospace 12
foreground_color
/schemas/apps/gnome-terminal/profiles/Default/foreground_color
#000000
icon
/schemas/apps/gnome-terminal/profiles/Default/icon
gnome-terminal.png
login_shell
/schemas/apps/gnome-terminal/profiles/Default/login_shell
false
no_aa_without_render
/schemas/apps/gnome-terminal/profiles/Default/no_aa_without_render
true
palette
/schemas/apps/gnome-terminal/profiles/Default/palette
#2E2E34343636:#CCCC00000000:#4E4E9A9A0606:#C4C4A0A00000:#34346565A4A4:#757550507B7B:#060698209A9A:#D3D3D7D7CFCF:#555557575353:#EFEF29292929:#8A8AE2E23434:#FCFCE9E94F4F:#72729F9FCFCF:#ADAD7F7FA8A8:#3434E2E2E2E2:#EEEEEEEEECEC
scroll_background
/schemas/apps/gnome-terminal/profiles/Default/scroll_background
true
scroll_on_keystroke
/schemas/apps/gnome-terminal/profiles/Default/scroll_on_keystroke
true
scroll_on_output
/schemas/apps/gnome-terminal/profiles/Default/scroll_on_output
false
scrollback_lines
/schemas/apps/gnome-terminal/profiles/Default/scrollback_lines
500
scrollbar_position
/schemas/apps/gnome-terminal/profiles/Default/scrollbar_position
right
silent_bell
/schemas/apps/gnome-terminal/profiles/Default/silent_bell
false
title
/schemas/apps/gnome-terminal/profiles/Default/title
Hold
title_mode
/schemas/apps/gnome-terminal/profiles/Default/title_mode
replace
update_records
/schemas/apps/gnome-terminal/profiles/Default/update_records
true
use_custom_command
/schemas/apps/gnome-terminal/profiles/Default/use_custom_command
false
use_skey
/schemas/apps/gnome-terminal/profiles/Default/use_skey
true
use_system_font
/schemas/apps/gnome-terminal/profiles/Default/use_system_font
true
use_theme_colors
/schemas/apps/gnome-terminal/profiles/Default/use_theme_colors
true
visible_name
/schemas/apps/gnome-terminal/profiles/Default/visible_name
Hold
word_chars
/schemas/apps/gnome-terminal/profiles/Default/word_chars
-A-Za-z0-9,./?%%&#:_
x_font
/schemas/apps/gnome-terminal/profiles/Default/x_font
-misc-fixed-medium-r-semicondensed--*-120-*-*-c-*-*-*
'''
TEMPLATE_EXIM_CONF = r"""# /etc/exim4/update-exim4.conf.conf
#
# Edit this file and /etc/mailname by hand and execute update-exim4.conf
# yourself or use 'dpkg-reconfigure exim4-config'
#
# Please note that this is _not_ a dpkg-conffile and that automatic changes
# to this file might happen. The code handling this will honor your local
# changes, so this is usually fine, but will break local schemes that mess
# around with multiple versions of the file.
#
# update-exim4.conf uses this file to determine variable values to replace
# the DEBCONFsomethingDEBCONF strings in the configuration template files.
#
# Most settings found in here do have corresponding questions in the
# Debconf configuration, but not all of them.
#
# This is a Debian specific file
dc_eximconfig_configtype='smarthost'
dc_other_host_names='%(host_name)s'
dc_local_interfaces='127.0.0.1'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost='%(smarthost)s::%(port)s'
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname='false'
dc_mailname_in_oh='true'
dc_localdelivery='mail_spool'
"""
TEMPLATE_EXIM_PASSWORD = r"""# /etc/passwd.client
#
# Password file used when the local exim is authenticating to a remote
# host as a client.
#
# see exim4_passwd_client(5) for more documentation
#
# Example:
### target.mail.server.example:login:password
%(autho_reverse_domain)s:%(autho_user)s:%(autho_password)s
"""
TEMPLATE_RSYNCD = """
# This is a basic rsync configuration file
# It exports a single module without user authentication.
motd file = /home/rsync/welcome.msg
use chroot = yes
[localhost]
path = /home/rsync
comment = Default rsync module
read only = yes
list = yes
uid = rsyncd
gid = rsyncd
"""
TEMPLATE_HYLAFAX_CONFIG = """
LogFacility:\t\tdaemon
CountryCode:\t\t1
AreaCode:\t\t%(fax_area_code)s
LongDistancePrefix:\t1
InternationalPrefix:\t011
DialStringRules:\tetc/dialrules
ServerTracing:\t\t1
"""
TEMPLATE_HYLAFAX_MODEM = """
# $Id: usr-xon,v 1.11 2005/11/29 21:28:57 lhoward Exp $
#
# HylaFAX Facsimile Software
#
# Copyright (c) 1990-1996 Sam Leffler
# Copyright (c) 1991-1996 Silicon Graphics, Inc.
# HylaFAX is a trademark of Silicon Graphics, Inc.
#
# Permission to use, copy, modify, distribute, and sell this software and
# its documentation for any purpose is hereby granted without fee, provided
# that (i) the above copyright notices and this permission notice appear in
# all copies of the software and related documentation, and (ii) the names of
# Sam Leffler and Silicon Graphics may not be used in any advertising or
# publicity relating to the software without the specific, prior written
# permission of Sam Leffler and Silicon Graphics.
#
# THE SOFTWARE IS PROVIDED \"AS-IS\" AND WITHOUT WARRANTY OF ANY KIND,
# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
#
# IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
# ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
# LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE.
#
#
# Configuration for using the Class 1 command interface with
# a USR Courier or Sportster modem and XON/XOFF flow control.
#
#
CountryCode:\t\t1
AreaCode:\t\t%(fax_area_code)s
FAXNumber:\t\t%(fax_phone)s
LongDistancePrefix:\t1
InternationalPrefix:\t011
DialStringRules:\tetc/dialrules
ServerTracing:\t\t1
SessionTracing:\t\t11
RecvFileMode:\t\t0600
LogFileMode:\t\t0600
DeviceMode:\t\t0600
RingsBeforeAnswer:\t3
SpeakerVolume:\t\tmedium
GettyArgs:\t\t\"-h %%l dx_%%s\"
LocalIdentifier:\t%(fax_id)s
TagLineFont:\t\tetc/lutRS18.pcf
TagLineFormat:\t\t\"From %%%%l|%%c|Page %%%%P of %%%%T\"
MaxRecvPages:\t\t25
#
#
# Modem-related stuff: should reflect modem command interface
# and hardware connection/cabling (e.g. flow control).
#
ModemType:\t\tClass1\t\t# use class 1 interface
ModemRate:\t\t19200\t\t# rate for DCE-DTE communication
ModemFlowControl:\txonxoff\t\t# software flow control
#
ModemSetupDTRCmd:\tATS13=1&D2\t# setup so DTR drop resets modem
ModemSetupDCDCmd:\tAT&C1\t\t# setup so DCD reflects carrier (or not)
ModemNoFlowCmd:\t\tAT&H0&I0&R1\t# setup modem for no flow control
ModemHardFlowCmd:\tAT&H1&I0&R2\t# setup modem for hardware flow control
ModemSoftFlowCmd:\tAT&H2&I2&R1\t# setup modem for software flow control
ModemResultCodesCmd:\tATQ0X4\t\t# enable result codes
#
ModemMfrQueryCmd:\t!USR
ModemModelQueryCmd:\tATI3
ModemRevQueryCmd:\tATI7\t\t# XXX returns a multi-line result
#
# When AT+FCLASS=1 is issued the modem automatically switches
# to software flow control; these parameters let the fax software
# reset flow control as needed after entering Class 1.
#
Class1NFLOCmd:\t\tAT&H0&I0&R1\t# setup modem for no flow control
Class1HFLOCmd:\t\tAT&H1&I0&R2\t# setup modem for hardware flow control
Class1SFLOCmd:\t\t""\t\t# modem does this automatically
#
# This should resolve \"DIS/DTC received 3 times\" errors:
#
Class1ResponseWaitCmd:\tAT+FRS=1\t# wait after sending TCF for response
#
# The remainder of this configuration is included so that the
# modem \"idles\" in Class 0 while not sending or receiving facsimile.
#
ModemSetupAACmd:\tAT+FCLASS=0\t# leave modem idling in class 0
ModemAnswerCmd:\t\tAT+FCLASS=1A\t# answer in Class 1
#
# When using AT+FRS=n we see USR modems reset themselves in the middle of sessions
# this is not good. So, we seem to work-around that problem by not using the
# command. Unfortunately, this isn't an ideal thing.
#
Class1SwitchingCmd:\t\"\"
"""
TEMPLATE_ALSA_MIXER = """pcm.card0 {
type hw
card 0
}
pcm.!default {
type plug
slave.pcm \"dmixer\"
}
pcm.dmixer {
type dmix
ipc_key 1025
slave {
pcm \"hw:0,0\"
period_time 0
period_size 2048\t#1024
buffer_size 32768\t#4096
\t\t\t#periods 128
rate 48000\t\t#44100
}
bindings {
0 0
1 1
}
}
"""
TEMPLATE_ESOUND = r"""[esd]
auto_spawn=1
spawn_options=-terminate -nobeeps -as 2 -d default
spawn_wait_ms=100
# default options are used in spawned and non-spawned mode
default_options=
"""
TEMPLATE_EMACS = r"""(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
"""
TEMPLATE_GNUBIFF = r"""
"""
TEMPLATE_MAILBOX_EMPTY = """iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAKnRFWHRDcmVhdGlvbiBUaW1lAE1p
IDMgU2VwIDIwMDMgMTA6NTU6NDggKzAxMDDmBteMAAAAB3RJTUUH1AcHCBk7TpQBtAAAAAlwSFlz
AAALEQAACxEBf2RfkQAAAARnQU1BAACxjwv8YQUAAAQNSURBVHja7VXPS2NXGP3yEl9iNI6tTieK
WrURYZABl+KmzLpbN/V/KF12MwuXheKqpYtClw4D3Q0Up4VSlJYicdTiNNoYJzGOJho15nfez55z
+94gM+kM3XTVC4eX3HfvOd/5vu/eJ/L/eMsI/Jv5xcVFbWFhoatQKESCwWA4Go3G2+32CMC5Z0tL
SzksMwG3I1EymZyq1Wpf9PX1feg4TpdpmtJoNJqu64Y0TQsbhhGybRs/NQkEAhKJRAK9vb2CtXJ6
emrv7e09WllZebCzs/MCdG1yhnzytbW1mXq9/vPw8PAAFsjQ0JDMz8/zVffb0oAAKBgcGxv7uFKp
DKVSqU8QTJoiQW9BYHt7+3uQfrC8vCy5XE62traEQhSBK0VCwIFgswJSI61WS5rNpnoODg7SzUSx
WKxmMhkKVJXA7Ozs/fHx8c9WV1cFeZVYLCZIkyKBKxkdHZVqtaoEfGIf/O+LcCBtcnh4qG9sbKTw
91SlKBQK3We++ZI5vWkdi2Vubk65IBGKK5ZlKSf+0wdFuCcej09jO7GtBLBwmItJzuLdHNwIy4qM
m/25TkBDKA40gY5lg0BYCTAyEofD4dcE2CF+GkhCpw5rQVAMjWhD17VsiWnoUDhF6iy8oohGgQA2
qZbqJOBHzVrYWlCaDoptmBL4IyU9mZTEiwdy+ywjDqLff/CNNGI9DML2z4JygAmDT13XOwow7w1E
OPD4oYz/+kTePT+VW33Y3M1TBQeOJu1rW67Taem+N0PHvoDr18Dg4WGRO/U4BXg8Z5KP5Sh/Ic+q
IgXtPbljadKtW9J4URJpIDtIIU6nL+D4DsDhGiTyBXwXfHKeztxmQ3746FMpnRelmkiI2xOTme++
lF6jLMcFCKDhdbR2G/VCraybAiyKwWJSgKQEe5y9f3V1pQrroouMqUmJvD8iEaw1Q7p0a5boVku1
iomYKiCPohZwYHoCzssU+aQsJnuencO0+Q4oYmLOBTnOjWgIxq1cS7BVF50scHBUKDjHW5vb6+vr
v2OmzPKGvFaslUolmZ6eVtHz9+Xl5d+RQ4CEbFG65Hs1D7HycQnRN9x0K/T8oaU9/fHrryp1RijC
WzUJXKurAmTZcrlcw23qnJycDIAwgktPJicnpb+/Xx2efD6vROiS4rnMwcWjQO9Pn+/kf/m2bv65
Z9sZCD/lvQlsAM+Bmt+TdBIH7qFjEhMTE2NwMwKBu1NTU3cTiURwf39fzs7OWul0+rfNzc2jbDbb
hrtz7MkCB8AxcEVS76p2X/0eUKQH4GV0C7gDjCL6MYgM8xzu7u5aiLLqpSDjkV94pC11sF8Znb5c
nNN4MQJRT/Ad4Lb3vgigL6Uiqvvl5amVfyB70/Dfd3l3S8Czb76J9D8dfwGCF56MUIpd7AAAAABJ
RU5ErkJggg=="""
TEMPLATE_MAILBOX_FULL = """iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAKnRFWHRDcmVhdGlvbiBUaW1lAE1p
IDMgU2VwIDIwMDMgMTA6NTU6NDggKzAxMDDmBteMAAAAB3RJTUUH1AcHCBk7TpQBtAAAAAlwSFlz
AAALEQAACxEBf2RfkQAAAARnQU1BAACxjwv8YQUAAATqSURBVHjavVVLbFRVGP7mzr0zwwztAG1J
H5YK08EWKCZKkAZjFSwaYqIm1KSJCxbozrWJ6MKNwYQYZOECCRsTYliIiTESrAEt0gLVdkzT0mYI
pQ4tTB/zujOd+/Y7d2a0bVDQhTf5cs49/zn/4/v//xzgv32eMh5p4yN/Cw2hI/NaccftrKVpHhSL
DgoZE2NvAz9QXCTs1Wfkf2PAt7n20GNO8qXfby5hvQ8IKsBkmspVzFA8Qairz0gPoWAF1KIh2RZn
Dl0V4Pyez1fTffDgKz09Pdsp8a9mZcXPlStXWgzDOB4Oh7sdx/Hpuo4lfrZtywiu9def/EBpif8o
XYvrCDGCNTwzu3Mv6k5+jsTkpDUxMXHu7Nmz78disbsUaSsoGhgYiFBXf0tLS8PIyAhqamrQ1dUl
RGsqe2a/qCnRTLc8AoyiujqMp9vbBbw815vNZhvGx8ffoXNxkZc/DeTz+dONjU0Nx459DMkrg17j
0uV+HD36HqPII1BVBZOcOFzPp0ruOSTY5wsgx0jNfB6RSASdnZ3P79u3780LFy6c4pZp18Cl/utP
1dbWdV3su4jG+lp4rBxtq9Bnr+LrMx+iu/ddzKezME0Dhmpj8/lv0HDgRWZQgqUVkU4mYRoGgsGg
a6S1tfUFwbgoPPnMEXwUO73nkKIEPdKSijqvm0NYZMI0gdhX32Hvy4ehSgFYVBLmetGyqPQe9EIB
FucVkGKXjfr6+ic4CIzIOQOvd+62o0V6zChZ3EQBEHvVXKnMJm58j/Ud+yGRHi8XEjMzsFqa6L22
wgBpZk6qGZjEEkCtqCpZzWNwcRFt6zY2I9S8B46/jWltp2gHDCWKxSx5rs6hkE/AL+qSBuYW5hHK
ZGDSgMiVAKvORS6Xg6Zppmgb0QZypojBVAqHm3bsQsdr55DKgJvgRpPLCJ7mSI0OzVThNUqNWtAN
LAl6SFlFsfiEIREFS90udQstpFPoT6XhJH477/a+u2yXIEmOCJfl6KFXSzAdesryFAp1KjeWQfSM
MGAycZybZQOOdOpn3MqmMO1jQDd/+gys0FKNe0p9KAwIexYPOlSkuJ1sw6DC1QbESHqEEatyL4kc
6oUirmZJS3zwBBT/Xwa83pIBYcimgts7n8O5J19FuDUCnYqWGxBRKYpS+TcrBkQfOLonGFtYKPQq
UhyLiWnIwU0rIhCjyXpH2zbYka1w2A8WDQpZIBCALMvc78Hc3JxbTYzCqBDtXnZV23vUhSSwthoY
v3wCsiK8d/uIo8LDsnvQrRpdg0yh3+9nF/tcSkRiE4kEJicn7b6+vl+JS1SbJgy3k6sefzY7E/sS
/oCGoW8/xTNvfEJlS8il77EfkqxtkTzHTaDwWtAgRlVVkUqlnGQyOTU8PPzL6OholmusP9whhoiM
S0RrdOu6/Q13j9taflf1pt3NHQfe2hBtj6KxqdlNnqDo2rXrrLKi+59hD1Dp4tjY2ABvTk6T4mqa
JW6VIeYp8T5UrmsRSSOxh55FeKPWtLW1NW7ZsmVbNBrdTsjxeBz379/XOA4ODQ3dmZqa0liW8zxz
mxA3Z6KiVBROJcnL3wMyjzARKmMj0RwKhTbxAmtiIgOkgAHoFQri5XG+rFQ8mdaDXq4HrUnl1ylI
rCU2EHVlWbKMrGjqslLnb17Ghz76FblSvls85ZfK+Cel/+v3B7RFv8Z4bYcVAAAAAElFTkSuQmCC"""
def is_root():
return os.geteuid() == ZERO
def go_home(file_name):
if GLOBAL_USER is None:
user_name = NULL
else:
user_name = GLOBAL_USER.name
return os.path.join('~%s' % user_name, file_name)
class ConfigFile(object):
def __init__(
self,
file_name=None,
coding='utf-8',
permissions=None,
):
if file_name is None:
self.file_name = None
else:
self.file_name = os.path.expanduser(file_name)
self.coding = coding
if (self.file_name is None) or (not os.path.exists(self.file_name)):
self.uid = NA
self.gid = NA
self.mode = None
else:
state = os.stat(self.file_name)
self.uid = state.st_uid
self.gid = state.st_gid
self.mode = stat.S_IMODE(state.st_mode)
if permissions is None:
self.permissions = self.mode
else:
self.permissions = permissions
self.text = None
self.is_dirty = False
return
def get_unit(self, mode='r'):
return codecs.open(self.file_name, mode, self.coding)
def set_permissions(self):
if self.permissions is None:
pass
else:
os.chmod(self.file_name, self.permissions)
return self
def load(self):
try:
unit = self.get_unit()
self.text = unit.read()
unit.close()
except IOError:
self.text = None
self.is_dirty = False
return self
def save(self):
if self.is_dirty:
self.roll_file()
unit = self.get_unit(mode='w')
unit.write(self.text)
unit.close()
self.set_permissions()
self.is_dirty = False
return self
def trash_file(self, path=None):
if path is None:
path = self.file_name
proc = subprocess.Popen(
args='trash %s' % path,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
return proc.wait()
def is_day_old(self, path):
if os.path.exists(path):
state = os.stat(path)
stamp = state.st_mtime
date = datetime.date.fromtimestamp(stamp)
result = (date < TODAY)
else:
result = False
return result
def roll_file(self, path=None):
if path is None:
path = self.file_name
bak = '%s~' % path
orig = '%s.original' % path
is_rolled = False
if os.path.exists(orig):
pass
else:
try:
os.rename(path, orig)
is_rolled = True
except OSError:
pass
if is_rolled:
pass
else:
if self.is_day_old(path) or (not os.path.exists(bak)):
self.trash_file(bak)
try:
os.rename(path, bak)
except OSError:
pass
return self
def set_dirty(self, state=True):
self.is_dirty = state
return self
def set_text(self, text):
self.text = text
self.set_dirty()
return self
def set_text_formatted(self, text, dict=None):
if dict is None:
result = self.set_text(text % self.__dict__)
else:
result = self.set_text(text % dict)
return result
def search_text(self, pattern):
if hasattr(pattern, 'match'):
pass
else:
pattern = re.compile(pattern)
return pattern.search(self.text)
def replace_text(self, pattern, replacement, count=1):
if hasattr(pattern, 'match'):
pass
else:
pattern = re.compile(pattern)
self.text = pattern.sub(replacement, self.text, count=count)
self.set_dirty()
return self
def insert_text(self, text, pos=ZERO):
if self.text is None:
self.text = text
else:
self.text = self.text[:pos] + text + self.text[pos:]
self.set_dirty()
return self
def append_text(self, text):
return self.insert_text(text, pos=sys.maxint)
class ConfigFileScript(ConfigFile):
sig = '%s . by vital_linux_tweaks.py' % TODAY_STRING
pat_tweaks_header = re.compile(r'^.*?by vital_linux_tweaks.*?\n')
pat_newline = re.compile(r'\n')
pat_shebang = re.compile(r'^#!(.*?)\n')
def __init__(
self,
file_name,
coding='utf-8',
permissions=None,
comment_interstitial='# ',
comment_inline=' # ',
):
ConfigFile.__init__(
self,
file_name=file_name,
coding=coding,
permissions=permissions,
)
self.comment_interstitial = comment_interstitial
self.comment_inline = comment_inline
return
def force_trailing_newline(self):
if self.text.endswith('\n'):
pass
else:
self.append_text('\n')
return self
def insert_sig_headline(self):
if self.comment_interstitial is None:
pass
else:
replacement = '%s%s . %s\n' % (self.comment_interstitial, self.file_name, self.sig)
if self.search_text(self.pat_tweaks_header) is None:
match = self.search_text(self.pat_shebang)
if match is None:
self.insert_text(replacement, pos=ZERO)
else:
self.insert_text(replacement, pos=match.end(1))
else:
self.replace_text(pat_tweaks_header, replacement)
return self
def wrap_sig_inline(self, text):
if self.comment_inline is None:
result = text
else:
replacement = '%s%s\n' % (self.comment_inline, self.sig)
result = self.pat_newline.sub(replacement, text, count=1)
return result
class ConfigFileJavaScript(ConfigFileScript):
pass
class ConfigFileJavaScriptFF(ConfigFileJavaScript):
pass
class Token(object):
def __init__(self, value, lpos):
self.value = value
self.is_list = None
self.lpos = lpos
self.rpos = None
return
def __str__(self):
return str(self.value)
class Tokens(list):
"""Tokenize Emacs Lisp.
Inspired by:
o Bjorndalen, Ole Martin. \"readlisp.py.\" 7 June 2001.
11 Nov. 2007. .
"""
def load(self, unit):
parser = shlex.shlex(unit)
parser.commenters = ';'
parser.quotes = '"'
parser.wordchars += '#.e+_-'
self.tokenize(unit, parser)
return self
def tokenize(self, unit, parser):
while True:
token = Token(parser.get_token(), unit.tell())
if token.value in ['(']:
token.value = Tokens().tokenize(unit, parser) # Push recursion level.
token.is_list = True
elif token.value in [')', None, NULL]:
break # Pop recursion level.
else:
token.is_list = False
token.rpos = unit.tell()
self.append(token)
return self
def has_token(self, key):
for token in self:
if token.value == key:
return True
return False
def find_sexp(self, key):
for token in self:
sexp = token.value
if isinstance(sexp, Tokens):
if sexp.has_token(key):
return token
else:
token = sexp.find_sexp(key)
if token is None:
pass
else:
return token
return None
def __str__(self):
values = [str(value) for value in self]
result = ', '.join(values)
return '[%s]' % result
class ConfigFileLisp(ConfigFile):
def load(self):
ConfigFile.load(self)
self.tokenize()
return self
def tokenize(self):
self.tokens = Tokens()
if self.text is None:
pass
else:
self.tokens.load(StringIO.StringIO(self.text))
return self
def find_sexp(self, key):
return self.tokens.find_sexp(key)
class ConfigFileXml(ConfigFile):
def __init__(
self,
file_name=None,
coding='utf-8',
permissions=None,
):
ConfigFile.__init__(
self,
file_name=file_name,
coding=coding,
permissions=permissions,
)
self.tree = None
return
def load(self):
self.tree = ElementTree.parse(self.file_name)
self.invert_namespace()
self.is_dirty = False
return self
def save(self):
if self.is_dirty:
self.roll_file()
if self.tree is None:
unit = self.get_unit(mode='w')
unit.write(self.text)
unit.close()
else:
unit = self.get_unit(mode='w')
self.tree.write(unit, self.coding)
unit.write('\n')
unit.close()
self.set_permissions()
self.is_dirty = False
return self
def findall(self, key):
return self.tree.findall(key)
def getroot(self):
return self.tree.getroot()
def append(self, elt):
self.getroot().append(elt)
return self
def invert_namespace(self):
self.namespace = {}
for (key, val) in ElementTree._namespace_map.iteritems():
self.namespace[val] = key
return self
def solve(self, tag):
tuple = tag.split(':')
if len(tuple) == 2:
(prefix, local) = tuple
uri = self.namespace.get(prefix.lower())
if uri is None:
result = tag
else:
result = '{%s}%s' % (uri, local)
else:
result = tag
return result
class ConfigFileIni(ConfigFile):
def __init__(
self,
file_name=None,
coding='utf-8',
permissions=None,
):
ConfigFile.__init__(
self,
file_name=file_name,
coding=coding,
permissions=permissions,
)
self.config = None
return
def load(self, defaults=None):
try:
unit = self.get_unit()
self.config = IniFileParser.ConfigParser(defaults)
self.config.readfp(unit)
unit.close()
except IOError:
self.config = None
self.is_dirty = False
return self
def save(self):
if self.is_dirty:
self.roll_file()
unit = self.get_unit(mode='w')
if self.config is None:
unit.write(self.text)
else:
self.config.write(unit)
unit.close()
self.set_permissions()
self.is_dirty = False
return self
class ConfigFileBase64(ConfigFile):
def get_unit(self, mode='r'):
return open(self.file_name, mode)
def save(self):
if self.is_dirty:
self.roll_file()
unit = self.get_unit(mode='wb')
unit.write(base64.decodestring(self.text))
unit.close()
self.set_permissions()
self.is_dirty = False
return self
class Role(object):
def __init__(self):
proc = subprocess.Popen(
args='hostname',
stdout=subprocess.PIPE,
shell=True,
)
retcd = proc.wait()
self.host_name = proc.stdout.read().strip()
return
def is_laptop(self):
host = self.get_host()
result = host.attribs.get('laptop', False)
return result
def is_desktop(self):
host = self.get_host()
result = host.attribs.get('desktop', False)
return result
def get_host(self):
for host in HOSTS:
if host.host_name == self.host_name:
return host
raise KeyError
class Change(object):
def __init__(self, name):
self.name = name
return
def enact(self):
self.show('%s:' % self.name.ljust(32))
return self
def show(self, text):
sys.stdout.write(text)
sys.stdout.flush()
return self
class ChangeMkdir(Change):
def __init__(self, name, path, mode=0755):
Change.__init__(self, name)
self.path = os.path.expanduser(path)
self.mode = mode
return
def enact(self):
Change.enact(self)
if os.path.exists(self.path):
self.show('"%s" exists.\n' % self.path)
else:
os.makedirs(self.path, self.mode)
self.show('"%s" created.\n' % self.path)
return self
class ChangeSymbolicLink(Change):
def __init__(self, name, source, destination):
Change.__init__(self, name)
self.source = os.path.expanduser(source)
self.destination = os.path.expanduser(destination)
return
def enact(self):
Change.enact(self)
(src_dir, src_file) = os.path.split(self.source)
(dst_dir, dst_file) = os.path.split(self.destination)
if os.path.isdir(self.destination):
self.destination = os.path.join(self.destination, src_file)
if os.path.islink(self.destination):
src_old = os.readlink(self.destination)
src_old = os.path.join(dst_dir, src_old)
if src_old == self.source:
self.show('"%s" already linked to "%s."\n' % (self.destination, self.source))
else:
ConfigFile(self.destination).roll_file()
os.symlink(self.source, self.destination)
self.show('"%s" link changed to "%s."\n' % (self.destination, self.source))
else:
if os.path.exists(self.destination):
self.show('"%s" is an actual path and can\'t be linked.\n' % self.destination)
else:
os.symlink(self.source, self.destination)
self.show('"%s" linked to "%s."\n' % (self.destination, self.source))
return self
class ChangeRemovefile(Change):
def __init__(self, name, file_name):
Change.__init__(self, name)
self.file_name = os.path.expanduser(file_name)
return
def enact(self):
Change.enact(self)
if os.path.exists(self.file_name):
ConfigFile().trash_file(self.file_name)
self.show('"%s" deleted.\n' % self.file_name)
else:
self.show('"%s" not found.\n' % self.file_name)
return self
class ChangeCommand(Change):
def __init__(self, name, command):
Change.__init__(self, name)
self.command = command
return
def enact(self):
Change.enact(self)
proc = subprocess.Popen(
args=self.command,
shell=True,
)
retcd = proc.wait()
self.show('"%s" run with returncode=%i.\n' % (self.command, retcd))
return self
class ChangeGConf(Change):
get = {
bool: gconf.Client.get_bool,
int: gconf.Client.get_int,
str: gconf.Client.get_string,
list: gconf.Client.get_list,
}
set = {
bool: gconf.Client.set_bool,
int: gconf.Client.set_int,
str: gconf.Client.set_string,
list: gconf.Client.set_list,
}
def __init__(self, name):
Change.__init__(self, name)
self.client = gconf.client_get_default()
return
def set_gconf(self, key, value):
type_ = type(value)
if isinstance(value, list):
if isinstance(value[ZERO], str):
list_type = gconf.VALUE_STRING
elif isinstance(value[ZERO], int):
list_type = gconf.VALUE_STRING
else:
list_type = None
self.set[type_](self.client, key, list_type, value)
else:
self.set[type_](self.client, key, value)
return self
def get_gconf(self, key, type_, list_type=None):
if list_type is None:
result = self.get[type_](self.client, key)
else:
result = self.get[type_](self.client, key, list_type)
return result
class ChangeGConfKeyValue(ChangeGConf):
def __init__(self, name, key, value):
ChangeGConf.__init__(self, name)
self.key = key
self.value = value
return
def enact(self):
Change.enact(self)
type_ = type(self.value)
if isinstance(self.value, list):
if isinstance(self.value[ZERO], str):
list_type = gconf.VALUE_STRING
elif isinstance(self.value[ZERO], int):
list_type = gconf.VALUE_STRING
else:
list_type = None
old_value = self.get_gconf(self.key, type_, list_type=list_type)
else:
old_value = self.get_gconf(self.key, type_)
if old_value == self.value:
self.show('"%s" already %s.\n' % (self.key, self.value))
else:
self.set_gconf(self.key, self.value)
self.show('"%s" set to %s.\n' % (self.key, self.value))
return self
class RefreshRate(object):
def __init__(self, rate, current):
rate = float(rate)
rate = '%i' % int(rate)
self.rate = rate
self.current = current
return
def __str__(self):
if self.current:
result = '*%s' % self.rate
else:
result = self.rate
return result
class RefreshRates(dict):
def parse_line(self, line):
rates = line.split()
for rate in rates:
if rate.endswith('*'):
obj = RefreshRate(rate[:-1], True)
else:
obj = RefreshRate(rate, False)
self[obj.rate] = obj
return self
def __str__(self):
items = [str(val) for val in self.itervalues()]
items.sort()
return SPACE.join(items)
class Resolution(object):
def __init__(self, hor, ver, rates):
self.hor = hor
self.ver = ver
self.rates = rates
return
def __str__(self):
result = '%sx%s %s' % (self.hor, self.ver, self.rates)
return result
class Resolutions(dict):
# pat_randr = re.compile(r'\s*(\*?)\d*\s*(\d*)\s*x\s*(\d*)\s*\([^)]*\)\s*(.*)')
pat_randr = re.compile(r'\s*(\d*)\s*x\s*(\d*)\s*(.*)')
def parse_line(self, line):
match = self.pat_randr.match(line)
if match is None:
pass
else:
(hor, ver, refresh_rates) = match.group(1, 2, 3)
rates = RefreshRates().parse_line(refresh_rates)
obj = Resolution(hor, ver, rates)
key = '%sx%s' % (obj.hor, obj.ver)
self[key] = obj
return self
def __str__(self):
items = [str(val) for val in self.itervalues()]
items.sort()
return '\n'.join(items)
class ChangeScreenMode(ChangeGConf):
pat_res = re.compile(r'(\d*)\s*x\s*(\d*)')
def __init__(self, name, res, refresh):
ChangeGConf.__init__(self, name)
match = self.pat_res.search(res)
(self.hor, self.ver) = match.group(1,2)
self.refresh = refresh
return
def enact(self):
Change.enact(self)
proc = subprocess.Popen(
args='xrandr -q',
stdout=subprocess.PIPE,
shell=True,
)
retcd = proc.wait()
lines = proc.stdout.readlines()
resolutions = Resolutions()
for line in lines:
resolutions.parse_line(line)
key = '%sx%s' % (self.hor, self.ver)
req_res = resolutions.get(key)
if req_res is None:
req_refresh = None
else:
req_refresh = req_res.rates.get(self.refresh)
if None in [req_res, req_refresh]:
self.show('"%sx%s %sHz" is not available.\n' %
(self.hor, self.ver, self.refresh))
else:
if req_refresh.current:
self.show('"%sx%s %sHz" is already selected.\n' %
(self.hor, self.ver, self.refresh))
else:
self.set_gconf(
key='/desktop/gnome/screen/default/0/resolution',
value='%sx%s' % (self.hor, self.ver),
)
self.set_gconf(
key='/desktop/gnome/screen/default/0/rate',
value=int(self.refresh),
)
command = 'xrandr --size %sx%s --rate %s' % \
(self.hor, self.ver, self.refresh)
proc = subprocess.Popen(
args=command,
shell=True,
)
retcd = proc.wait()
self.show('"%sx%s %sHz" selected with returncode=%i.\n' %
(self.hor, self.ver, self.refresh, retcd))
return self
class ChangeEmacs(Change):
def __init__(self, name, key, value):
Change.__init__(self, name)
self.key = key
self.value = value
return
def enact(self):
Change.enact(self)
init_file = ConfigFileLisp(go_home('.emacs'))
init_file.load()
if init_file.text is None:
init_file.text = TEMPLATE_EMACS
init_file.tokenize()
sexp = init_file.find_sexp(self.key)
if sexp is None:
sexp = init_file.find_sexp('custom-set-variables')
if sexp is None:
self.show('"custom-set-variables" not found.\n')
else:
init_file.insert_text("'(%s %s)\n " % (self.key, self.value), sexp.rpos - 1)
self.show('"%s" initialized to %s.\n' % (self.key, self.value))
else:
prolog = init_file.text[:sexp.lpos]
epilog = init_file.text[sexp.rpos:]
sexp_text = init_file.text[sexp.lpos:sexp.rpos]
pat = re.compile(r'%s\s*(.+?)\)$' % self.key, re.DOTALL)
match = pat.search(sexp_text)
if match is None:
self.show('"%s" could not be set.\n' % self.key)
else:
old_value = match.group(1)
if old_value == self.value:
self.show('"%s" already %s.\n' % (self.key, self.value))
else:
repl = '%s %s)' % (self.key, self.value)
sexp_text = pat.sub(repl, sexp_text)
init_file.text = prolog + sexp_text + epilog
init_file.set_dirty()
self.show('"%s" changed to %s.\n' % (self.key, self.value))
init_file.save()
return self
class ChangeGConfLoadXml(ChangeGConf):
def __init__(self, name, text):
ChangeGConf.__init__(self, name)
self.text = text
return
def enact(self):
ChangeGConf.enact(self)
proc = subprocess.Popen(
args='gconftool-2 --load=-',
stdin=subprocess.PIPE,
shell=True,
)
proc.stdin.write(self.text)
proc.stdin.close()
self.retcd = proc.wait()
return self
class ChangeAddNewPanelApp(ChangeGConfLoadXml):
"""Add an Application to a Panel.
Inspired by:
o McLoughlin, Mark. Reply to Brian Cameron. \"Re: GConf Stability
- How to Add Icons to Panel.\" Desktop Devel . 4 Feb. 2005. 13 Nov. 2007.
.
"""
key = '/apps/panel/general/applet_id_list'
def __init__(
self,
name,
app,
bonobo_iid,
right_stick=True,
panel='top_panel_screen0',
text=TEMPLATE_XML_APP,
):
self.app_name = app
self.bonobo_iid = bonobo_iid
self.right_stick=right_stick
self.panel=panel
xml = ConfigFileXml().set_text_formatted(text, dict=self.__dict__)
ChangeGConfLoadXml.__init__(self, name, xml.text)
return
def enact(self):
ChangeGConfLoadXml.enact(self)
old_value = self.get_gconf(
key=self.key,
type_=list,
list_type=gconf.VALUE_STRING,
)
if self.app_name in old_value:
self.show('"%s" already installed.\n' % self.app_name)
else:
old_value.append(self.app_name)
self.set_gconf(
key=self.key,
value=old_value,
)
if self.retcd in [ZERO]:
self.show('"%s" installed with returncode=%i.\n' % (
self.app_name,
self.retcd,
))
else:
self.show('"%s" not installed with returncode=%i.\n' % (
self.app_name,
self.retcd,
))
return self
class ChangeAddNewPanelAppSystemMonitor(ChangeAddNewPanelApp):
def __init__(
self,
name,
app,
bonobo_iid,
right_stick=True,
panel='top_panel_screen0',
text=TEMPLATE_XML_APP_SYSTEM_MONITOR,
):
self.view_load_cpu = True
self.view_load_memory = False
self.view_load_io = False
self.view_load_network = False
self.view_load_swap = False
self.view_load_average = False
ChangeAddNewPanelApp.__init__(
self,
name=name,
app=app,
bonobo_iid=bonobo_iid,
right_stick=right_stick,
panel=panel,
text=text,
)
return
class ChangeDelPanelObj(ChangeGConf):
"""Remove a launcher from a panel.
"""
key = '/apps/panel/general/object_id_list'
def __init__(
self,
name,
obj_name,
):
ChangeGConf.__init__(self, name)
self.obj_name=obj_name
return
def enact(self):
ChangeGConf.enact(self)
old_value = self.get_gconf(
key=self.key,
type_=list,
list_type=gconf.VALUE_STRING,
)
if self.obj_name in old_value:
old_value.remove(self.obj_name)
self.set_gconf(
key=self.key,
value=old_value,
)
self.show('"%s" disabled.\n' % self.obj_name)
else:
self.show('"%s" not enabled.\n' % self.obj_name)
return self
class ChangeFFProfileName(Change):
"""Move the random Mozilla profile name to a standard location.
Symlink the old name to the standard name.
This makes it possible to clone Mozilla installations.
"""
def __init__(self, name):
Change.__init__(self, name)
self.path = os.path.expanduser(go_home('.mozilla/firefox'))
return
def enact(self):
Change.enact(self)
old_profile_name = old_profile_link = self.get_profile_name()
new_profile_name = os.path.join(self.path, 'Profile0.default')
is_old_link = os.path.islink(old_profile_name)
if is_old_link:
old_profile_name = os.path.realpath(old_profile_name)
is_old_profile = os.path.exists(old_profile_name)
is_new_profile = os.path.exists(new_profile_name)
if old_profile_name == new_profile_name:
self.show('"%s" already points to "%s."\n' % (old_profile_link, new_profile_name))
else:
if is_new_profile:
if is_old_profile:
if is_old_link:
pass
else:
ConfigFile(old_profile_name).roll_file()
else:
if is_old_profile:
os.rename(old_profile_name, new_profile_name)
# print 'new=%s, old=%s' % (new_profile_name, old_profile_link)
os.symlink(new_profile_name, old_profile_link)
self.show('"%s" linked to "%s."\n' % (old_profile_link, new_profile_name))
return self
def get_profile_name(self):
ini = ConfigFileIni(file_name=os.path.join(self.path, 'profiles.ini'))
ini.load()
section = None
for ndx in ['0', '1', '2']:
section = 'Profile%s' % ndx
if ini.config.has_option(section, 'Path'):
break
else:
raise NotImplementedError
profile_name = ini.config.get(section, 'Path')
return os.path.join(self.path, profile_name)
class ChangeFFUserPref(Change):
def __init__(self, name, key, value):
Change.__init__(self, name)
self.key = key
self.value = value
self.pattern = re.compile(r'^user_pref\("%s",\s*(.*?)\);$' % self.key, re.MULTILINE)
self.replacement = 'user_pref("%s", %s);' % (self.key, self.value)
return
def enact(self):
Change.enact(self)
init_file = ConfigFileJavaScriptFF(go_home('.mozilla/firefox/Profile0.default/user.js'))
init_file.load()
if init_file.text is None:
init_file.text = NULL
match = init_file.search_text(self.pattern)
if match is None:
init_file.insert_text("%s\n" % self.replacement)
self.show('"%s" initialized to %s.\n' % (self.key, self.value))
else:
old_value = match.group(1)
if old_value == self.value:
self.show('"%s" already %s.\n' % (self.key, self.value))
else:
init_file.replace_text(self.pattern, self.replacement)
self.show('"%s" changed to %s.\n' % (self.key, self.value))
init_file.save()
return self
class ChangeFFLocalStore(Change):
def __init__(self, name, key, **attributes):
Change.__init__(self, name)
self.key = key
self.attributes = attributes
return
def enact(self):
Change.enact(self)
init_file = ConfigFileXml(go_home('.mozilla/firefox/Profile0.default/localstore.rdf'))
init_file.load()
descriptions = init_file.findall(init_file.solve('RDF:Description'))
elts = [
desc
for desc in descriptions
if desc.get(init_file.solve('RDF:about')) == 'chrome://browser/content/browser.xul#%s' % self.key
]
if elts:
elt = elts[-1]
for (key, value) in self.attributes.iteritems():
elt.set(key, value)
init_file.set_dirty()
init_file.save()
self.show('"%s" set to %s.\n' % (self.key, str(self.attributes)))
else:
elt = ElementTree.Element(init_file.solve('RDF:Description'))
elt.set(init_file.solve('RDF:about', 'chrome://browser/content/browser.xul#%s' % self.key))
for (key, value) in self.attributes.iteritems():
elt.set(key, value)
init_file.append(elt)
init_file.set_dirty()
init_file.save()
self.show('"%s" initialized to %s.\n' % (self.key, str(self.attributes)))
return self
class ChangeDisablePrefBarButtons(Change):
def __init__(self, name, key):
Change.__init__(self, name)
self.key = key
return
def enact(self):
Change.enact(self)
init_file = ConfigFileXml(go_home('.mozilla/firefox/Profile0.default/prefbar.rdf'))
init_file.load()
sequences = init_file.findall(init_file.solve('RDF:Seq'))
seq_enabled = None
seq_disabled = None
for seq in sequences:
if seq.get(init_file.solve('RDF:about')) == 'urn:prefbar:browserbuttons:enabled':
seq_enabled = seq
if seq.get(init_file.solve('RDF:about')) == 'urn:prefbar:browserbuttons:disabled':
seq_disabled = seq
if None in [seq_enabled, seq_disabled]:
self.show('"%s" could not be disabled.\n' % self.key)
else:
buttons = seq_enabled.findall(init_file.solve('RDF:li'))
target = 'urn:prefbar:buttons:%s' % self.key
for button in buttons:
if button.get(init_file.solve('RDF:resource')) == target:
seq_enabled.remove(button)
seq_disabled.insert(ZERO, button)
init_file.set_dirty()
init_file.save()
self.show('"%s" button disabled.\n' % self.key)
break
else:
self.show('"%s" button not found or already disabled.\n' % self.key)
return self
class ChangeEnablePrefBarButtons(Change):
def __init__(self, name, key, before):
Change.__init__(self, name)
self.key = key
self.before = before
return
def enact(self):
Change.enact(self)
init_file = ConfigFileXml(go_home('.mozilla/firefox/Profile0.default/prefbar.rdf'))
init_file.load()
sequences = init_file.findall(init_file.solve('RDF:Seq'))
seq_enabled = None
seq_disabled = None
for seq in sequences:
if seq.get(init_file.solve('RDF:about')) == 'urn:prefbar:browserbuttons:enabled':
seq_enabled = seq
if seq.get(init_file.solve('RDF:about')) == 'urn:prefbar:browserbuttons:disabled':
seq_disabled = seq
if None in [seq_enabled, seq_disabled]:
self.show('"%s" could not be enabled.\n' % self.key)
else:
buttons = seq_enabled.findall(init_file.solve('RDF:li'))
target = 'urn:prefbar:buttons:%s' % self.before
for (ndx, button) in enumerate(buttons):
if button.get(init_file.solve('RDF:resource')) == target:
before_button = ndx
break
else:
before_button = None
if before_button is None:
self.show('"%s" place could not be found.\n' % self.before)
else:
buttons = seq_disabled.findall(init_file.solve('RDF:li'))
target = 'urn:prefbar:buttons:%s' % self.key
for button in buttons:
if button.get(init_file.solve('RDF:resource')) == target:
seq_disabled.remove(button)
seq_enabled.insert(before_button, button)
init_file.set_dirty()
init_file.save()
self.show('"%s" button enabled.\n' % self.key)
break
else:
self.show('"%s" button not found or already enabled.\n' % self.key)
return self
class ChangeBashPrompt(Change):
reg_bash_prompt = 'source ~/.bash_prompt.sh'
pat_bash_prompt = re.compile(r'^%s' % reg_bash_prompt, re.MULTILINE)
def __init__(self, name):
Change.__init__(self, name)
return
def enact(self):
Change.enact(self)
script_name = go_home('.bash_prompt.sh')
script = ConfigFileScript(script_name)
script.load()
if script.text is None:
script.set_text(TEMPLATE_BASH_PROMPT)
script.insert_sig_headline()
script.save()
bashrc_name = go_home('.bashrc')
bashrc = ConfigFileScript(bashrc_name)
bashrc.load()
match = bashrc.search_text(self.pat_bash_prompt)
if match is None:
bashrc.force_trailing_newline()
command = bashrc.wrap_sig_inline('%s\n' % self.reg_bash_prompt)
bashrc.append_text(command)
bashrc.save()
self.show('Done.\n')
else:
self.show('Already set.\n')
return self
class ChangeIni(Change):
def __init__(self, name, ini_file_name, section, dict):
Change.__init__(self, name)
self.ini_file_name = ini_file_name
self.section = section
self.dict = dict
return
def enact(self):
Change.enact(self)
ini_file = ConfigFileIni(self.ini_file_name)
ini_file.load()
if ini_file.config.has_section(self.section):
pass
else:
ini_file.config.add_section(self.section)
ini_file.set_dirty()
for (key, value) in self.dict.iteritems():
if ini_file.config.has_option(self.section, key):
old_value = ini_file.config.get(self.section, key)
if old_value == value:
pass
else:
ini_file.config.set(self.section, key, value)
ini_file.set_dirty()
else:
ini_file.config.set(self.section, key, value)
ini_file.set_dirty()
if ini_file.is_dirty:
self.show('Section "%s" initialized/updated.\n' % self.section)
else:
self.show('Section "%s" unchanged.\n' % self.section)
ini_file.save()
return self
class ChangeScriptAppend(Change):
def __init__(self, name, script_name, command, **attribs):
Change.__init__(self, name)
self.script_name = script_name
self.command = command
self.attribs = attribs
return
def enact(self):
Change.enact(self)
init_file = ConfigFileScript(self.script_name, **self.attribs)
init_file.load()
if init_file.text is None:
init_file.set_text(self.command)
init_file.force_trailing_newline()
init_file.insert_sig_headline()
self.show('"%s" initialized.\n' % self.script_name)
else:
pat = re.compile(r'^%s' % self.command, re.MULTILINE)
match = init_file.search_text(pat)
if match is None:
init_file.force_trailing_newline()
command = init_file.wrap_sig_inline("%s\n" % self.command)
init_file.append_text(command)
self.show('"%s" appended to %s.\n' % (self.command, self.script_name))
else:
self.show('"%s" already in %s.\n' % (self.command, self.script_name))
init_file.save()
return self
class ChangeConfigOverwrite(Change):
def __init__(
self,
name,
init_file_name,
text,
is_formatted=True,
constructor=ConfigFileScript,
permissions=None,
comment_interstitial='# ',
comment_inline=' # ',
):
Change.__init__(self, name)
self.init_file_name = init_file_name
self.permissions = permissions
self.text = text
self.constructor = constructor
self.is_formatted = is_formatted
self.comment_interstitial = comment_interstitial
self.comment_inline = comment_inline
return
def enact(self):
Change.enact(self)
init_file = self.constructor(
self.init_file_name,
permissions=self.permissions,
)
if isinstance(init_file, ConfigFileScript):
init_file.comment_interstitial = self.comment_interstitial
init_file.comment_inline = self.comment_inline
if self.is_formatted:
init_file.set_text_formatted(self.text, dict=self.__dict__)
else:
init_file.set_text(self.text)
if isinstance(init_file, ConfigFileScript):
init_file.insert_sig_headline()
init_file.force_trailing_newline()
init_file.save()
self.show('"%s" overwritten.\n' % self.init_file_name)
return self
class ChangeNetInterface(ChangeConfigOverwrite):
def __init__(
self,
name,
host,
text=TEMPLATE_NETWORK_INTERFACES,
):
self.static_ip = host.static_ip
self.gateway = host.gateway
ChangeConfigOverwrite.__init__(
self,
name=name,
init_file_name='/etc/network/interfaces',
text=text,
)
return
class ChangeDevilsPieRule(ChangeConfigOverwrite):
def __init__(
self,
name,
key,
path,
test='contains',
property_='window_name',
action='maximize',
text = TEMPLATE_DEVILS_PIE,
):
self.key = key
self.path = path
self.test = test
self.property_ = property_
self.action = action
self.text = text
self.init_file_name = os.path.join(self.path, '%s_%s.ds' % (self.action, self.key))
ChangeConfigOverwrite.__init__(
self,
name,
init_file_name=self.init_file_name,
text= self.text,
comment_interstitial='; ',
comment_inline=None,
)
return
class ChangeSearchReplace(Change):
def __init__(
self,
name,
file_name,
targets,
permissions=None,
):
Change.__init__(self, name)
self.file_name = file_name
self.targets = targets
self.permissions = permissions
return
def enact(self):
Change.enact(self)
config_file = ConfigFile(self.file_name, permissions=self.permissions)
config_file.load()
if config_file.text is None:
pass
else:
for (target, replacement) in self.targets:
match = config_file.search_text(target)
if match is None:
pass
else:
config_file.replace_text(target, replacement)
if config_file.is_dirty:
self.show('Targets replaced in %s.\n' % self.file_name)
else:
self.show('Targets not found in %s this time.\n' % self.file_name)
config_file.save()
return self
class ChangeDesktop(ChangeConfigOverwrite):
def __init__(
self,
name,
desktop_name,
path=None,
icon=NULL,
categories=NULL,
comment=NULL,
text=None,
):
self.desktop_name = desktop_name
self.icon = icon
self.categories = categories
self.comment = comment
if path is None:
init_file_name = go_home('Desktop/my-%s.desktop' % self.desktop_name)
else:
init_file_name = go_home(path)
ChangeConfigOverwrite.__init__(
self,
name,
init_file_name=init_file_name,
constructor=ConfigFileIni,
text=text,
)
return
class ChangeDesktopLauncher(ChangeDesktop):
def __init__(
self,
name,
desktop_name,
exec_,
path=None,
icon=NULL,
categories=NULL,
comment=NULL,
text=TEMPLATE_DESKTOP_LAUNCHER,
):
self.exec_ = exec_
ChangeDesktop.__init__(
self,
name=name,
desktop_name=desktop_name,
path=path,
icon=icon,
categories=categories,
comment=comment,
text=text,
)
return
class ChangeDesktopLauncherTerm(ChangeDesktopLauncher):
def __init__(
self,
name,
desktop_name,
exec_,
path=None,
icon=NULL,
categories=NULL,
comment=NULL,
text=TEMPLATE_DESKTOP_LAUNCHER_TERM,
):
ChangeDesktopLauncher.__init__(
self,
name=name,
desktop_name=desktop_name,
exec_=exec_,
path=path,
icon=icon,
categories=categories,
comment=comment,
text=text,
)
return
class ChangeDesktopLink(ChangeDesktop):
def __init__(
self,
name,
desktop_name,
link,
path=None,
icon=NULL,
categories=NULL,
comment=NULL,
text=TEMPLATE_DESKTOP_LINK,
):
self.link = link
ChangeDesktop.__init__(
self,
name=name,
desktop_name=desktop_name,
path=path,
icon=icon,
categories=categories,
comment=comment,
text=text,
)
return
class ChangeNewsId(ChangeConfigOverwrite):
def __init__(
self,
name,
id,
email,
sig_command,
text=TEMPLATE_PAN_ID,
):
self.id = id
self.email = email
self.sig_command = sig_command
init_file_name=go_home('.pan2/posting.xml')
ChangeConfigOverwrite.__init__(
self,
name,
init_file_name=init_file_name,
text=text,
constructor=ConfigFileXml,
)
return
class ChangeHosts(Change):
pat_host = re.compile(r'^\s*127.0.0.1\s+([^\s#]{,60}).*$', re.MULTILINE)
repertoire = [
'http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext',
'http://someonewhocares.org/hosts/hosts',
# 'http://www.mvps.org/winhelp2002/hosts.txt',
]
buffer=r'''#
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
'''
def __init__(self, name):
Change.__init__(self, name)
return
def enact(self):
Change.enact(self)
domains = []
for url in self.repertoire:
domains.extend(self.get_hosts(url))
domains = self.elim_dups(domains)
lines = ['127.0.0.1\tlocalhost']
for host in HOSTS:
lines.append('%s\t%s %s' % (host.static_ip, host.fqdn, host.host_name))
lines.append(self.buffer)
lines.append('# Blacklist Internet popup and advertising hosts:')
for domain in domains:
lines.append('127.0.0.1 %s' % domain)
init_file_name = '/etc/hosts'
init_file = ConfigFileScript(init_file_name)
init_file.set_text('\n'.join(lines))
init_file.insert_sig_headline()
init_file.force_trailing_newline()
init_file.save()
self.show('"%s" replaced (%i entries).\n' % (init_file_name, len(domains)))
return self
def get_hosts(self, url):
result = []
unit = urllib2.urlopen(url)
text = unit.read()
unit.close()
lines = self.pat_host.split(text)
while True:
if lines:
lines.pop(ZERO)
if lines:
result.append(lines.pop(ZERO))
else:
break
return result
def elim_dups(self, domains):
domains.sort()
result = []
while domains:
domain = domains.pop(ZERO)
result.append(domain)
while domains:
if domains[ZERO] == domain:
domains.pop(ZERO)
else:
break
return result
class ChangePrinter(ChangeCommand):
def __init__(
self,
name,
printer_name,
caption,
physical_location,
connection,
model,
driver,
):
command = 'foomatic-configure -n "%s" -N "%s" -L "%s" -c "%s" -p "%s" -d "%s"' % (
printer_name,
caption,
physical_location,
connection,
model,
driver,
)
ChangeCommand.__init__(self, name, command)
return
class ChangeMimeAssociations(Change):
pat_mime_cache = re.compile(r'\[MIME Cache\]')
def __init__(
self,
name,
path_local_apps,
):
Change.__init__(self, name)
self.path_local_apps = path_local_apps
return
def enact(self):
Change.enact(self)
mimeinfo_cache_file_name = os.path.join(self.path_local_apps, 'mimeinfo.cache')
defaults_list_file_name = os.path.join(self.path_local_apps, 'defaults.list')
old = ConfigFile(mimeinfo_cache_file_name)
old.load()
new = ConfigFile(defaults_list_file_name)
new.set_text(old.text)
new.replace_text(self.pat_mime_cache, '[Default Applications]')
new.save()
self.show('"%s" created.\n' % defaults_list_file_name)
return
class ChangeBootScripts(Change):
def __init__(
self,
name,
key,
start_kill,
run_levels=None,
):
Change.__init__(self, name)
self.key = key
if start_kill.lower() in ['start']:
self.new_sentinel = 'S'
self.old_sentinel = 'K'
elif start_kill.lower() in ['kill']:
self.new_sentinel = 'K'
self.old_sentinel = 'S'
else:
raise NotImplementedError
self.disable = self.new_sentinel in ['K']
self.run_levels = run_levels
if self.run_levels is None:
self.run_levels = [2, 3, 4, 5]
return
def enact(self):
Change.enact(self)
pat = re.compile(r'^%s(\d{2})%s$' % (self.old_sentinel, self.key))
is_dirty = False
for run_level in self.run_levels:
path = '/etc/rc%i.d' % run_level
for old_symbolic_link in os.listdir(path):
match = pat.match(old_symbolic_link)
if match is None:
pass
else:
old_seq = int(match.group(1), 10)
new_seq = 100 - old_seq
new_symbolic_link = '%s%02i%s' % (self.new_sentinel, new_seq, self.key)
old_symbolic_link = os.path.join(path, old_symbolic_link)
new_symbolic_link = os.path.join(path, new_symbolic_link)
# print 'mv %s %s' % (old_symbolic_link, new_symbolic_link)
os.rename(old_symbolic_link, new_symbolic_link)
is_dirty = True
if is_dirty:
if self.disable:
self.show('"%s" disabled.\n' % self.key)
else:
self.show('"%s" enabled.\n' % self.key)
else:
if self.disable:
self.show('"%s" already disabled.\n' % self.key)
else:
self.show('"%s" already enabled.\n' % self.key)
return self
class ChangeWallpaper(ChangeGConfLoadXml):
def __init__(
self,
name,
picture=NULL,
colors=('#006699', '#0099CC'),
style='scaled',
text=TEMPLATE_WALLPAPER,
):
self.picture = picture
self.style = style
self.color_top = colors[ZERO]
self.color_bottom = colors[1]
xml = ConfigFileXml().set_text_formatted(text, dict=self.__dict__)
ChangeGConfLoadXml.__init__(self, name, xml.text)
return
def enact(self):
ChangeGConfLoadXml.enact(self)
self.show('Wallpaper loaded with returncode=%i.\n' % self.retcd)
return self
class ChangeGnomeTerminalProfile(ChangeGConfLoadXml):
def __init__(
self,
name,
profile_name='Hold',
text=TEMPLATE_TERM_PROFILE_TRANSPARENT_HOLD,
):
self.profile_name = profile_name
xml = ConfigFileXml().set_text_formatted(text, dict=self.__dict__)
ChangeGConfLoadXml.__init__(self, name, xml.text)
return
def enact(self):
ChangeGConfLoadXml.enact(self)
self.show('"Hold" profile loaded with returncode=%i.\n' % self.retcd)
return self
class ChangeExim(Change):
'''Configure *exim4* (*sendmail*).
Do the work of *dpkg-reconfigure exim4-config* as described in:
Zugschlus [Marc Haber.] "Debian Exim4 User FAQ." 16
Aug. 2007. _Debian Wiki_. 15 Dec. 2007
.
The end result is a peculiarly placed config at */var/lib/exim4/config.autogenerated*.
'''
def __init__(
self,
name,
mail_name,
host_name,
smarthost,
port,
autho_reverse_domain,
autho_user,
autho_password,
):
Change.__init__(self, name)
self.mail_name = mail_name
self.host_name = host_name
self.smarthost = smarthost
self.port = port
self.autho_reverse_domain = autho_reverse_domain
self.autho_user = autho_user
self.autho_password = autho_password
return
def enact(self):
Change.enact(self)
mail_name_file = ConfigFile('/etc/mailname', permissions=0644)
mail_name_file.set_text('%s\n' % self.mail_name)
mail_name_file.save()
conf_conf_file = ConfigFileScript('/etc/exim4/update-exim4.conf.conf', permissions=0644)
conf_conf_file.set_text_formatted(TEMPLATE_EXIM_CONF, dict=self.__dict__)
conf_conf_file.insert_sig_headline()
conf_conf_file.save()
proc = subprocess.Popen(
args='update-exim4.conf',
shell=True,
)
retcd = proc.wait()
password_file = ConfigFileScript('/etc/exim4/passwd.client', permissions=0640)
password_file.set_text_formatted(TEMPLATE_EXIM_PASSWORD, dict=self.__dict__)
password_file.insert_sig_headline()
password_file.save()
self.show('Done with returncode=%i.\n' % retcd)
return self
class ChangeXml(Change):
def __init__(
self,
name,
init_file_name,
path,
tags=('name', 'value'),
attributes=None,
):
Change.__init__(self, name)
self.init_file_name = init_file_name
self.path = path
self.var_name = tags[ZERO]
self.val_name = tags[1]
self.attributes = attributes
return
def enact(self):
Change.enact(self)
doc = ConfigFileXml(self.init_file_name)
doc.load()
if doc.tree is None:
self.show('"%s" not found.\n' % self.init_file_name)
else:
elts = doc.findall(self.path)
if elts:
for elt in elts:
key = elt.get(self.var_name)
old_val = elt.get(self.val_name)
if key in self.attributes:
new_val = self.attributes[key]
if old_val == new_val:
pass
else:
elt.set(self.val_name, new_val)
doc.set_dirty()
if doc.is_dirty:
self.show('"%s" attributes set.\n' % self.path)
else:
self.show('"%s" attributes not set.\n' % self.path)
else:
self.show('"%s" not present.\n' % self.path)
doc.save()
return self
class ChangeGnuBiffXml(ChangeXml):
pass
class ChangeUdevPersistentDeviceName(Change):
def __init__(
self,
name,
device,
mac,
init_file_name='/etc/udev/rules.d/70-persistent-net.rules'
):
Change.__init__(self, name)
self.device = device
self.mac = mac
self.init_file_name = init_file_name
return
def enact(self):
Change.enact(self)
self.replacement = 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", NAME="%s"' % \
(self.mac, self.device)
pat_mac = re.compile(
r'^SUBSYSTEM=="net", ACTION=="add", DRIVERS=="\?\*", ATTR{address}=="%s",.*?NAME="([^"]*)"$' %
self.mac,
re.MULTILINE,
)
pat_dev = re.compile(
r'^SUBSYSTEM=="net", ACTION=="add", DRIVERS=="\?\*", ATTR{address}=="([^"]*)",.*?NAME="%s"$' %
self.device,
re.MULTILINE,
)
init_file = ConfigFileScript(self.init_file_name)
init_file.load()
if init_file.text is None:
init_file.set_text_formatted(
r'''
#
# You can modify this file, as long as you keep each rule on a single line.
# MAC addresses must be written in lowercase.
# %(name)s
%(replacement)s
''',
dict=self.__dict__)
init_file.insert_sig_headline()
self.show('"%s" created.\n' % init_file_name)
else:
match = init_file.search_text(pat_mac)
if match is None:
match = init_file.search_text(pat_dev)
if match is None:
text = '''
# %(name)s
%(replacement)s
''' % self.__dict__
init_file.append_text(text)
self.show('Device %s added to "%s."\n' % (self.device, self.init_file_name))
else:
init_file.replace_text(pat_dev, self.replacement)
self.show('"%s" changed from %s to %s.\n' % (self.device, match.group(1), self.mac))
elif self.device == match.group(1):
self.show('"%s" already %s.\n' % (self.mac, match.group(1)))
else:
init_file.replace_text(pat_mac, self.replacement)
self.show('"%s" changed from %s to %s.\n' % (self.mac, match.group(1), self.device))
init_file.save()
return self
class ChangeHylafax(Change):
def __init__(
self,
name,
fax_area_code=FAX_AREA_CODE,
fax_phone=FAX_PHONE,
fax_id=FAX_ID,
fax_modem=FAX_MODEM,
):
self.fax_area_code = fax_area_code
self.fax_phone = fax_phone
self.fax_id = fax_id
self.fax_modem = fax_modem
Change.__init__(self, name)
return
def enact(self):
Change.enact(self)
hylafax_config_file = ConfigFileScript('/etc/hylafax/config')
hylafax_config_file.set_text_formatted(TEMPLATE_HYLAFAX_CONFIG, dict=self.__dict__)
hylafax_config_file.insert_sig_headline()
hylafax_config_file.save()
hylafax_modem_file = ConfigFileScript('/etc/hylafax/config.%s' % self.fax_modem)
hylafax_modem_file.set_text_formatted(TEMPLATE_HYLAFAX_MODEM, dict=self.__dict__)
hylafax_modem_file.insert_sig_headline()
hylafax_modem_file.save()
self.show('"/etc/hylafax/config*" created.\n')
return self
# Mainline begins here.
MACHINE = Role()
print MACHINE.host_name
sys.stdout.write('Please be sure nothing else is running (such as a browser).\n')
IS_PANEL_DIRTY = False
IS_DESKTOP_DIRTY = False
if not is_root():
sys.exit('... requires root authority')
GLOBAL_USER = None
if when('obsolete'):
ChangeMkdir(
name='Make wastebasket',
path=go_home('.Trash'),
).enact()
if when('configuration'):
ChangeMkdir(
name='Make mount-point for "guest"',
path='/mnt/guest',
).enact()
if when('configuration'):
ChangeIni(
name='Enable root login to *gdm*',
ini_file_name='/etc/gdm/gdm.conf',
section='security',
dict={'AllowRoot': 'true'},
).enact()
if when('look_and_feel'):
ChangeBashPrompt(
name='Change *bash* prompt',
).enact()
if when('communications'):
ChangeCommand(
name='Allow users to *wvdial*',
command='chmod a+rx /usr/sbin/pppd',
).enact()
ChangeCommand(
name='Allow users to *wvdial*',
command='chmod a+s /usr/sbin/pppd',
).enact()
ChangeScriptAppend(
name='Allow users to *wvdial*',
script_name='/etc/ppp/options',
command='privgroup dialout',
).enact()
if when('communications'):
ChangeUdevPersistentDeviceName(
name='Persistent name for Wifi',
device='wifi0',
mac=WIFI_MAC,
).enact()
if MACHINE.is_laptop():
ChangeUdevPersistentDeviceName(
name='Persistent name for ETH0',
device='eth0',
mac=ETH0_MAC_LAPTOP,
).enact()
else:
ChangeUdevPersistentDeviceName(
name='Persistent name for ETH0',
device='eth0',
mac=ETH0_MAC_DESKTOP,
).enact()
ChangeNetInterface(
name='Configure LAN and PPP',
host=MACHINE.get_host(),
).enact()
if when('long'):
ChangeHosts(
name='Block Internet ad hosts (long)',
).enact()
if when('look_and_feel'):
ChangeSearchReplace(
name='Screensaver default pix folder',
file_name='/usr/share/applications/screensavers/personal-slideshow.desktop',
targets=[
('Exec=slideshow .*\n',
'Exec=slideshow --location=%s\n' % SCREENSAVER_PERSONAL_SLIDE_SHOW_FRAMES),
],
).enact()
if when('configuration'):
ChangeScriptAppend(
name='Add Debian Backports Channel',
script_name='/etc/apt/sources.list',
command='deb http://www.backports.org/debian etch-backports main contrib non-free',
comment_interstitial=None,
comment_inline=None,
).enact()
if when('configuration'):
ChangeMkdir(
name='Make .mc folder.',
path=go_home('.mc'),
).enact()
ChangeScriptAppend(
name='*mc* FTP hotlink for Web site',
script_name=go_home('.mc/hotlist'),
command='ENTRY "%s" URL "/#ftp:%s:%s@%s/vservers/%s"' %
(FQDN, FTP_USER, FTP_PASSWORD, FQDN, FTP_USER),
).enact()
if when('configuration'):
ChangeConfigOverwrite(
name='Config *rsyncd*',
init_file_name='/etc/rsyncd.conf',
text=TEMPLATE_RSYNCD,
is_formatted=False,
).enact()
if when('browser'):
ChangeCommand(
name='Make FF the default browser',
command='update-alternatives --set x-www-browser /usr/bin/iceweasel',
).enact()
# ChangeCommand(
# name='Make FF the default browser',
# command='update-alternatives --set gnome-www-browser /usr/bin/iceweasel',
# ).enact()
# Debian 5.0 (Lenny) offers one and only one "alternative" for
# *gnome-www-browser*. Nuts to that noise! *gnome-www-browser* is
# needed to render text/html mimetypes when, for instance, *mutt*
# asks *gnome* to display an attachment. There is no earthly
# reason why *iceweasel* can't do that job as well as *epiphany*
# does.
ChangeSymbolicLink(
name='Make FF the default browser',
source='/usr/bin/iceweasel',
destination='/etc/alternatives/gnome-www-browser',
).enact()
if when('configuration'):
ChangeBootScripts(
name='Disable *HylaFAX* daemons',
key='hylafax',
start_kill='kill',
).enact()
ChangeBootScripts(
name='Disable *freshclam* daemon',
key='clamav-freshclam',
start_kill='kill',
).enact()
ChangeBootScripts(
name='Disable *exim4* daemon',
key='exim4',
start_kill='kill',
).enact()
ChangeBootScripts(
name='Disable *fetchmail* daemon',
key='fetchmail',
start_kill='kill',
).enact()
ChangeBootScripts(
name='Disable *ssh* daemon',
key='ssh',
start_kill='kill',
).enact()
ChangeBootScripts(
name='Disable *rsync* daemon',
key='rsync',
start_kill='kill',
).enact()
if when('printing'):
ChangeConfigOverwrite(
name='Make *cups* a *foomatic* spooler.',
init_file_name='/etc/foomatic/defaultspooler',
text='cups\n',
is_formatted=False,
constructor=ConfigFile,
).enact()
ChangePrinter(
name='Add CUPS Printer',
printer_name='BJC-240L',
caption='Canon BJC-240L',
physical_location='Office',
connection='parallel:/dev/lp0',
model='Canon-BJC-240',
driver='bjc600',
).enact()
if when('mail'):
ChangeExim(
name='Configure *sendmail*',
mail_name=FQDN,
host_name=DOMAIN,
smarthost=SMARTHOST_USER,
port='50',
autho_reverse_domain='*.safesecureweb.com',
autho_user='root@%s' % FQDN,
autho_password=SMARTHOST_PASSWORD,
).enact()
ChangeCommand(
name='Set *sendmail* owners',
command='chown -R root:Debian-exim /etc/exim4/passwd.client',
).enact()
if when('mail'):
ChangeCommand(
name='Create mail log',
command='touch /var/log/procmail.log',
).enact()
ChangeCommand(
name='Set mail log permissions',
command='chmod 666 /var/log/procmail.log',
).enact()
if when('personal'):
for script in [
'origip.py',
'is_mail_fresh.py',
'open_mailto.py',
]:
ChangeSymbolicLink(
name='Make %s executable for *procmail*' % script,
source='/home/crhode/lab/email_admin/%s' % script,
destination='/usr/bin',
).enact()
if when('sound'):
# Inspired by:
#
# o Varunus. "Howto: Hear Multiple Sounds at Once." 22
# Dec. 2004. Online posting. Ubuntu Forums. 7 Jan. 2008
# .
ChangeConfigOverwrite(
name='Config *alsa* mixer',
init_file_name='/etc/asound.conf',
text=TEMPLATE_ALSA_MIXER,
is_formatted=False,
constructor=ConfigFile,
).enact()
ChangeConfigOverwrite(
name='Config *esd*',
init_file_name='/etc/esound/esd.conf',
text=TEMPLATE_ESOUND,
is_formatted=False,
constructor=ConfigFile,
).enact()
if when('look_and_feel', 'sound'):
ChangeGConfKeyValue(
name='Make *rhythmbox* default audio CD player',
key='/desktop/gnome/volume_manager/autoplay_cda_command',
value='rhythmbox %d',
).enact()
IMG_NEW_MAIL = '/usr/share/gnubiff/mailbox_full.png'
IMG_NO_MAIL = '/usr/share/gnubiff/mailbox_empty.png'
if when('mail'):
ChangeConfigOverwrite(
name='Create mailbox icon',
init_file_name=IMG_NEW_MAIL,
text=TEMPLATE_MAILBOX_FULL,
is_formatted=False,
constructor=ConfigFileBase64,
).enact()
ChangeConfigOverwrite(
name='Create mailbox icon',
init_file_name=IMG_NO_MAIL,
text=TEMPLATE_MAILBOX_EMPTY,
is_formatted=False,
constructor=ConfigFileBase64,
).enact()
if when('fax'):
ChangeHylafax(
name='Config *HylaFAX*',
).enact()
ChangeSymbolicLink(
name='Make *spadmin* executable',
source='/usr/lib/openoffice/program/spadmin',
destination='/usr/bin',
).enact()
ChangeSearchReplace(
name='Create *HylaFAX* pidfile',
file_name='/etc/init.d/hylafax',
targets=[
('''\n\nexit 0''',
'''\n\nrm -f /var/run/hylafax.pid || true # %s . by vital_linux_tweaks.py
pgrep faxq > /var/run/hylafax.pid || true
exit 0''' % TODAY_STRING),
('\$\{FAXMODEM\} `echo \$device \| cut -d . -f 2` >/dev/null 2>\&1 /var/run/hylafax.pid',
).enact()
if when('config'):
for config_file in [
'/boot/grub/menu.lst',
'/etc/X11/xorg.conf',
'/etc/network/interfaces',
'/etc/udev/rules.d/70-persistent-net.rules',
'/etc/fstab',
'/etc/hostname',
]:
ChangeCommand(
name='Contingency copy of %s' % (config_file),
command='cp -a %s %s.%s' % (config_file, config_file, MACHINE.host_name),
).enact()
ChangeCommand(
name='List installed packages',
command='dpkg --get-selections > /etc/debian_installed_packages.%s' % MACHINE.host_name,
).enact()
if when('obsolete'):
if MACHINE.is_laptop():
ChangeSearchReplace(
name='Set screen color depth',
file_name='/etc/X11/xorg.conf',
targets=[
(r'DefaultDepth\s*\d*', 'DefaultDepth 16'),
],
).enact()
elif MACHINE.is_desktop():
ChangeSearchReplace(
name='Set screen color depth',
file_name='/etc/X11/xorg.conf',
targets=[
(r'DefaultDepth\s*\d*', 'DefaultDepth 24'),
],
).enact()
if when('obsolete'):
ChangeRemovefile(
name='Disable bitmapped fonts (in FF)',
file_name='/etc/fonts/conf.d/70-yes-bitmaps.conf',
).enact()
ChangeSymbolicLink(
name='Disable bitmapped fonts (in FF)',
source='/etc/fonts/conf.avail/70-no-bitmaps.conf',
destination='/etc/fonts/conf.d',
).enact()
if when('obsolete'):
ChangeCommand(
name='Make *emacs* the default editor',
command='update-alternatives --set editor /usr/bin/emacs21',
).enact()
if when('obsolete'):
for GLOBAL_USER in USERS:
ChangeCommand(
name='Give %s dialout privilege' % GLOBAL_USER.name,
command='adduser %s dialout' % GLOBAL_USER.name,
).enact()
for GLOBAL_USER in USERS:
sys.stdout.write('\n... for User %s\n' % GLOBAL_USER.name)
os.setgid(GLOBAL_USER.gid)
os.setuid(GLOBAL_USER.uid)
if when('obsolete'):
ChangeMkdir(
name='Make wastebasket',
path=go_home('.Trash'),
).enact()
if when('obsolete'):
ChangeGConfKeyValue(
name='Enable ',
key='/apps/metacity/global_keybindings/run_command_9',
value='Delete',
).enact()
ChangeGConfKeyValue(
name='Enable ',
key='/apps/metacity/keybinding_commands/command_9',
value='gnome-system-monitor',
).enact()
if when('communications'):
DIALING_VOLUME = '1'
ChangeIni(
name='Config PPP',
ini_file_name=go_home('.wvdial.conf'),
section='Dialer Defaults',
dict={
'Modem': '/dev/%s' % FAX_MODEM,
'Phone': ISP_PHONE,
'Dial Command': 'ATM1L%sDT' % DIALING_VOLUME,
'Password': PPP_PASSWORD,
'Username': PPP_USER,
'Dial Attempts': '3',
'Ask Password': 'off',
'Auto Reconnect': 'on',
},
).enact()
ChangeSearchReplace(
name='Config PPP',
file_name=go_home('.wvdial.conf'),
targets=[
('\n;Dock = off\n', '\n;Dock = on\n'),
],
).enact()
if when('look_and_feel'):
ChangeAddNewPanelAppSystemMonitor(
name='Add System Monitor to panel',
app='system_monitor_screen0',
bonobo_iid='OAFIID:GNOME_MultiLoadApplet',
).enact()
if when('look_and_feel'):
ChangeBashPrompt(
name='Change *bash* prompt',
).enact()
if when('editor'):
ChangeEmacs(
name='*emacs* active region highlight',
key='transient-mark-mode',
value='t',
).enact()
ChangeEmacs(
name='*emacs* paren match highlight',
key='show-paren-mode',
value='t nil (paren)',
).enact()
ChangeEmacs(
name='*emacs* syntax highlight',
key='global-font-lock-mode',
value='t nil (font-lock)',
).enact()
if when('look_and_feel'):
ChangeScriptAppend(
name='Start *devilspie*',
script_name=go_home('.gnomerc'),
command='devilspie &',
).enact()
PATH_DEVILSPIE_RULES = go_home('.devilspie')
ChangeMkdir(
name='Make %s' % PATH_DEVILSPIE_RULES,
path=PATH_DEVILSPIE_RULES,
).enact()
# You can psych out the window_class of an application by running
# *xprop* from a non-maximized terminal window. Move xprop's
# cross-cursor to the title bar of the target app and click there.
ChangeDevilsPieRule(
name='Maximize *emacs*',
property_='window_class',
test='is',
key='Emacs',
path=PATH_DEVILSPIE_RULES,
).enact()
ChangeDevilsPieRule(
name='Maximize *gnome-terminal*',
property_='window_class',
test='is',
key='Gnome-terminal',
path=PATH_DEVILSPIE_RULES,
).enact()
ChangeDevilsPieRule(
name='Maximize *gnucash*',
property_='window_class',
test='is',
key='Gnucash',
path=PATH_DEVILSPIE_RULES,
).enact()
if when('look_and_feel'):
ChangeMkdir(
name='Make "Pictures" folder',
path=go_home(SCREENSAVER_PERSONAL_SLIDE_SHOW_FRAMES)
).enact()
ChangeGConfKeyValue(
name='Personal-slideshow screensaver',
key='/apps/gnome-screensaver/themes',
value=['personal-slideshow'],
).enact()
if when('look_and_feel'):
ChangeWallpaper(
name='Change desktop background',
picture=GLOBAL_USER.wallpaper,
style=GLOBAL_USER.wallpaper_style,
colors=GLOBAL_USER.background_colors,
).enact()
IS_DESKTOP_DIRTY = True
if when('look_and_feel'):
ChangeGConfKeyValue(
name='Nautilus Pref List View',
key='/apps/nautilus/preferences/default_folder_viewer',
value='list_view',
).enact()
ChangeGConfKeyValue(
name='Nautilus Pref Show Hidden',
key='/desktop/gnome/file_views/show_hidden_files',
value=True,
).enact()
ChangeCommand(
name='Save system icon positions',
command=('cp -a "%s" /tmp/nautilus.xml'
% os.path.expanduser(go_home('.nautilus/metafiles/x-nautilus-desktop:%2F%2F%2F.xml'))),
).enact()
ChangeCommand(
name='Save user icon positions',
command=('cp -a "%s" /tmp/desktop.xml'
% os.path.expanduser(go_home('.nautilus/metafiles/file:%2F%2F%2Fhome%2Fcrhode%2FDesktop.xml'))),
).enact()
ChangeCommand(
name='Reset all folders',
command='rm -rf "%s"' % go_home('.nautilus/metafiles/*'),
).enact()
ChangeCommand(
name='Restore system icon positions',
command=('cp -a /tmp/nautilus.xml "%s"'
% os.path.expanduser(go_home('.nautilus/metafiles/x-nautilus-desktop:%2F%2F%2F.xml'))),
).enact()
ChangeCommand(
name='Restore user icon positions',
command=('cp -a /tmp/desktop.xml "%s"'
% os.path.expanduser(go_home('.nautilus/metafiles/file:%2F%2F%2Fhome%2Fcrhode%2FDesktop.xml'))),
).enact()
IS_DESKTOP_DIRTY = True
if when('news'):
ChangeConfigOverwrite(
name='Set *pan* news servers',
init_file_name=go_home('.pan2/servers.xml'),
text=TEMPLATE_PAN_SERVERS,
constructor=ConfigFileXml,
is_formatted=False,
).enact()
ChangeNewsId(
name='Set *pan* news client',
id=GLOBAL_USER.real_name,
email=GLOBAL_USER.email,
sig_command=GLOBAL_USER.sig,
).enact()
ChangeXml(
name='Change *pan* preferences',
init_file_name=go_home('.pan2/preferences.xml'),
path='flag',
attributes={
'smooth-scrolling': 'false',
}
).enact()
ChangeXml(
name='Change *pan* preferences',
init_file_name=go_home('.pan2/preferences.xml'),
path='string',
attributes={
'editor': 'emacs',
}
).enact()
if when('browser'):
ChangeFFProfileName(
name='Alter FF profile name',
).enact()
ChangeFFUserPref(
name='Set browser homepage',
key='browser.startup.homepage',
value='"http://www.altavista.com/"',
).enact()
ChangeFFUserPref(
name='Masquerade browser version',
key='general.useragent.extra.firefox',
value='"Firefox/3.0.6"',
).enact()
# ChangeFFUserPref(
# name='*mutt* handles "mailto:"',
# key='network.protocol-handler.expose.mailto',
# value='true',
# ).enact() # 2008 Aug 28
# ChangeFFUserPref(
# name='*mutt* handles "mailto:"',
# key='network.protocol-handler.app.mailto',
# value='"open_mailto.py"',
# ).enact() # 2008 Aug 28
ChangeFFLocalStore(
name='Hide Bookmarks Toolbar',
key='PersonalToolbar',
collapsed='true',
).enact()
if when('browser'):
ChangeDisablePrefBarButtons(
name='Disable FF PrefBar Save button',
key='savepage',
).enact()
ChangeDisablePrefBarButtons(
name='Disable FF PrefBar Sep',
key='separator11',
).enact()
ChangeDisablePrefBarButtons(
name='Disable FF PrefBar Cache button',
key='clearallcache',
).enact()
ChangeEnablePrefBarButtons(
name='Enable FF PrefBar Font+ button',
key='fontup',
before='separator8',
).enact()
ChangeEnablePrefBarButtons(
name='Enable FF PrefBar Font- button',
key='fontdown',
before='separator8',
).enact()
if when('browser'):
ChangeGConfKeyValue(
name='Default Gnome Browser',
key='/desktop/gnome/url-handlers/http/command',
value='x-www-browser %s',
).enact()
ChangeGConfKeyValue(
name='Default Gnome Browser',
key='/desktop/gnome/url-handlers/https/command',
value='x-www-browser %s',
).enact()
ChangeGConfKeyValue(
name='Default Gnome Browser',
key='/desktop/gnome/applications/browser/exec',
value='x-www-browser',
).enact()
if when('browser'):
PATH_LOCAL_APPS = os.path.expanduser(go_home('.local/share/applications'))
ChangeMkdir(
name='Make default mime DT files',
path=PATH_LOCAL_APPS,
).enact()
ALTS_REPERTOIRE = [
('gnome-www-browser', 'iceweasel'),
]
for (alt_name, app_name) in ALTS_REPERTOIRE:
ChangeSymbolicLink(
name='Alternate link',
source='/usr/share/applications/%s.desktop' % app_name,
destination='%s/%s.desktop' % (PATH_LOCAL_APPS, alt_name),
).enact()
APPS_REPERTOIRE = [
'evince',
'gedit',
'eog',
]
for app_name in APPS_REPERTOIRE:
ChangeSymbolicLink(
name='Application link',
source='/usr/share/applications/%s.desktop' % app_name,
destination='%s/' % PATH_LOCAL_APPS,
).enact()
ChangeCommand(
name='Update Mime cache',
command='update-desktop-database "%s"' % PATH_LOCAL_APPS,
).enact()
ChangeMimeAssociations(
name='Create defaults list',
path_local_apps=PATH_LOCAL_APPS,
).enact()
if when('mail'):
MAIL_DIR = os.path.expanduser(go_home('Mail'))
ChangeMkdir(
name='Make mail folder',
path=go_home('Mail'),
).enact()
ChangeCommand(
name='Set mail folder permissions',
command='chmod 777 %s' % MAIL_DIR,
).enact()
ChangeCommand(
name='Set mailbox owners',
command='chown -R %s:%s %s' % (GLOBAL_USER.name, GLOBAL_USER.name, MAIL_DIR),
).enact()
ChangeCommand(
name='Set mailbox privileges',
command='chmod 640 %s/*' % MAIL_DIR
).enact()
if when('mail'):
GNU_BIFF_RUN_COMMANDS = go_home('.gnubiffrc')
GNU_BIFF_INBOX = os.path.expanduser(go_home('Mail/inbox'))
ChangeConfigOverwrite(
name='Create *gnubiff* config',
init_file_name=GNU_BIFF_RUN_COMMANDS,
text=TEMPLATE_GNUBIFF,
is_formatted=False,
constructor=ConfigFileXml,
).enact()
ChangeSearchReplace(
name='Fix bad *gnubiff* xml',
file_name=GNU_BIFF_RUN_COMMANDS,
targets=[
('additional_lines"value', 'additional_lines" value'),
],
).enact()
ChangeGnuBiffXml(
name='Config *gnubiff*',
init_file_name=GNU_BIFF_RUN_COMMANDS,
path='mailbox/parameter',
attributes={
'name':'inbox',
'username':GLOBAL_USER.name,
'address':GNU_BIFF_INBOX,
'protocol':'file',
'local_fam_enable':'true',
}
).enact()
ChangeGnuBiffXml(
name='Config *gnubiff*',
init_file_name=GNU_BIFF_RUN_COMMANDS,
path='general/parameter',
attributes={
'double_command':'gnome-terminal --title eMail -x mutt -f %s' % GNU_BIFF_INBOX,
'newmail_command':'esdplay /usr/share/sounds/ekiga/voicemail.wav',
}
).enact()
ChangeGnuBiffXml(
name='Config *gnubiff*',
init_file_name=GNU_BIFF_RUN_COMMANDS,
path='applet/parameter',
attributes={
'newmail_image': IMG_NEW_MAIL,
'newmail_text':' %d New',
'nomail_image': IMG_NO_MAIL,
'nomail_text':' %d New',
}
).enact()
ChangeAddNewPanelApp(
name='Add *gnubiff* to panel',
app='gnubiff_screen0',
bonobo_iid='OAFIID:GNOME_gnubiffApplet',
).enact()
IS_PANEL_DIRTY = True
if when('personal'):
# NOTA BENE: You have to "install" VaryOnApplet first.
ChangeCommand(
name='Kill *VaryOnApplet* instances',
command='killall VaryOnApplet.py && sleep 2',
).enact()
ChangeCommand(
name='Remove *VaryOnApplet* pidfiles',
command='rm -rf "%s"' % os.path.expanduser(go_home('.VaryOnApplet.0??.pid')),
).enact()
ChangeCommand(
name='Remove *VaryOnApplet* inifiles',
command='rm -rf "%s"' % os.path.expanduser(go_home('.VaryOnApplet.0??.ini')),
).enact()
ChangeConfigOverwrite(
name='Config *VaryOnApplet*',
init_file_name=go_home('.VaryOnApplet.099.ini'),
text=r"""Version='V1.4'
Start='gksu \"fetchmail --all --daemon 900\"'
Stop='gksu \"fetchmail --quit\"'
Lock='/var/run/fetchmail.pid'
Name='fetchmail'
Check=False
Title='fetchmail'
""",
is_formatted=False,
constructor=ConfigFile,
).enact()
ChangeAddNewPanelApp(
name='Add *VaryOnApplet* to panel',
app='VaryOnApplet_screen0',
bonobo_iid='OAFIID:GNOME_VaryOnApplet',
).enact()
ChangeConfigOverwrite(
name='Config *VaryOnApplet*',
init_file_name=go_home('.VaryOnApplet.098.ini'),
text=r"""Version='V1.4'
Start='gksu \"/etc/init.d/hylafax start\"'
Stop='gksu \"/etc/init.d/hylafax stop\"'
Lock='/var/run/hylafax.pid'
Name='faxq'
Check=True
Title='HylaFAX'
""",
is_formatted=False,
constructor=ConfigFile,
).enact()
ChangeAddNewPanelApp(
name='Add *VaryOnApplet* to panel',
app='VaryOnApplet_screen0',
bonobo_iid='OAFIID:GNOME_VaryOnApplet',
).enact()
IS_PANEL_DIRTY = True
if when('editor', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for *emacs*',
desktop_name='Emacs',
exec_='emacs %F',
icon='/usr/share/pixmaps/gnome-emacs.png',
categories='Text Editor',
comment='GNU Emacs is the extensible self-documenting text editor.',
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Apache Log',
desktop_name='Web Host Log',
exec_='/home/crhode/lab/web_host/ApacheLogRept.sh',
icon='/usr/share/pixmaps/gnome-log.png',
categories='System',
comment='Digest of clicks at HostMySite.com',
).enact()
IS_DESKTOP_DIRTY = True
if when('look_and_feel', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Washington Post',
desktop_name='Washington Post',
exec_='/home/crhode/lab/open_washington_post.py',
icon='/usr/share/pixmaps/gnome-news.png',
categories='Media',
comment='WaPo Website',
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
# Can't use a type=link *.desktop with this site. It serves a
# rotten Mime association causing it's content to be displayed
# by *gedit* rather than a browser.
ChangeDesktopLauncher(
name='Desktop icon for Sheboyan Press',
desktop_name='Sheboygan Press',
exec_='x-www-browser -new-tab "http://www.sheboygan-press.com/apps/pbcs.dll/frontpage"',
icon='/usr/share/pixmaps/gnome-news.png',
categories='Media',
comment='Gannett Company Website',
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
ChangeDesktopLink(
name='Desktop icon for Web Host',
desktop_name=FQDN,
link='ftp://%s@%s/vservers/%s' % (FTP_USER,FQDN,FTP_USER),
icon='/usr/share/icons/gnome/scalable/emblems/emblem-shared.svg',
comment='Web Host',
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
ChangeDesktopLink(
name='Desktop icon for Debian Users',
desktop_name='Debian Users',
link='http://forums.debian.net/index.php',
icon='/usr/share/pixmaps/gnome-debian.png',
categories='News',
comment='The Debian Way',
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
ChangeDesktopLink(
name='Desktop icon for Actual Riders',
desktop_name='Actual Riders',
link='http://actualriders.com/forums',
icon='/usr/share/pixmaps/gnome-note.png',
categories='News',
comment='Actual Riders',
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Weather',
desktop_name='Weather Observations',
exec_='x-www-browser -new-window "http://%s/WX/Observations.shtml?State=WI&County=WIC117&Zone=WIZ052&WFO=MKX&Station=KSBM"' % WWW,
icon='/usr/share/icons/gnome/scalable/status/weather-few-clouds.svg',
categories='Weather',
comment="Rocky Gnashtooth's Weather Observations and Prognostications",
).enact()
IS_DESKTOP_DIRTY = True
if when('browser', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Browser',
desktop_name='Firefox',
exec_='x-www-browser',
icon='/usr/share/icons/gnome/scalable/apps/web-browser.svg',
categories='Browser',
comment="Iceweasel 2.0.0.8",
).enact()
IS_DESKTOP_DIRTY = True
if when('communications', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Dialup',
desktop_name='Dialup',
exec_='gnome-ppp',
icon='/usr/share/icons/gnome/scalable/devices/modem.svg',
categories='Internet',
comment="GNOME Dialup Tool",
).enact()
IS_DESKTOP_DIRTY = True
if when('configuration', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Terminal',
desktop_name='Terminal',
exec_='x-terminal-emulator',
icon='/usr/share/icons/gnome/scalable/apps/gnome-terminal.svg',
categories='Terminal',
comment="GNOME Terminal",
).enact()
IS_DESKTOP_DIRTY = True
if when('news', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for *pan*',
desktop_name='Pan',
exec_='pan',
icon='pan.png',
categories='News',
comment="Pimp Ass Newsreader",
).enact()
IS_DESKTOP_DIRTY = True
if when('configuration', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for *mc*',
desktop_name='Midnight Commander',
exec_='gksu "gnome-terminal -x mc"',
icon='/usr/share/icons/gnome/scalable/devices/media-floppy.svg',
categories='System',
comment="Directory Browser / File Manager",
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Archive Prices',
desktop_name='Archive Prices',
exec_='/home/crhode/lab/email_admin/Move.sh prices',
icon='/usr/share/icons/gnome/scalable/apps/system-file-manager.svg',
categories='Mail',
comment='Append the "prices" folder to "archive.prices" and erase the original.',
).enact()
IS_DESKTOP_DIRTY = True
if when('printing', 'desktop_icons'):
ChangeDesktopLink(
name='Desktop icon for Printers',
desktop_name='Printing',
link='http://localhost:631/',
icon='/usr/share/icons/gnome/scalable/devices/printer.svg',
categories='System',
comment="Control Printer Queues",
).enact()
IS_DESKTOP_DIRTY = True
if when('personal', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Tonto',
desktop_name='Tonto',
exec_='/home/crhode/lab/tonto/Tonto.py',
icon='/usr/share/icons/gnome/scalable/status/dialog-information.svg',
categories='Addresses;Calendar;Notepad',
comment="Personal address-list, calendar, notepad, and so much more.",
).enact()
IS_DESKTOP_DIRTY = True
if when('look_and_feel'):
ChangeGnomeTerminalProfile(
name='Create *gnome-terminal* profile',
profile_name='Hold',
).enact()
CHANGE = ChangeGConfKeyValue(
name='Add *gnome-terminal* profile',
key='/apps/gnome-terminal/global/profile_list',
value=None,
)
PROFILE_LIST = CHANGE.get_gconf(CHANGE.key, type_=list, list_type=gconf.VALUE_STRING)
if 'Hold' in PROFILE_LIST:
pass
else:
PROFILE_LIST.append('Hold')
CHANGE.value = PROFILE_LIST
CHANGE.enact()
if when('personal', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for Raingauge',
desktop_name='Raingauge',
exec_=('gnome-terminal --window-with-profile=Hold --title="Raingauge" -x '
'%s KSBM' % '/home/crhode/lab/apples/raingauge.py'),
icon='/usr/share/icons/gnome/scalable/status/weather-showers-scattered.svg',
categories='Weather',
comment="Update database of hourly local weather conditions with recent observations.",
).enact()
IS_DESKTOP_DIRTY = True
if when('configuration', 'desktop_icons'):
ChangeDesktopLauncher(
name='Desktop icon for *clamav*',
desktop_name='Virus Scan',
exec_='gnome-terminal --window-with-profile=Hold --title="Virus Scan" -x gksu "freshclam --quiet; clamscan --recursive --infected --bell / | grep -v ^WARNING | grep -v ^ERROR"',
icon='/usr/share/icons/gnome/scalable/status/security-medium.svg',
categories='System',
comment="Clam AntiVirus",
).enact()
IS_DESKTOP_DIRTY = True
if when('mail', 'look_and_feel'):
ChangeSearchReplace(
name='Change *mutt* colors',
file_name=go_home('.muttrc'),
targets=[
(' blue white', ' blue default'),
(' red white', ' red default'),
(' black white', ' black default'),
(' green white', ' green default'),
],
).enact()
if when('mail', 'printing'):
ChangeScriptAppend(
name='Change *mutt* printer',
script_name=go_home('.muttrc'),
command='set print_command="lp -o page-top=48 -o page-bottom=48 -o page-left=48 -o page-right=48"',
).enact()
if when('look_and_feel'):
ChangeDelPanelObj(
name='Remove button for *evolution*',
obj_name='email_launcher_screen0',
).enact()
ChangeDelPanelObj(
name='Remove button for *epiphany*',
obj_name='browser_launcher_screen0',
).enact()
IS_PANEL_DIRTY = True
if when('look_and_feel'):
ChangeScreenMode(
name='Set screen resolution',
res='1024x768',
refresh='85',
).enact()
if when('fax'):
ChangeConfigOverwrite(
name='Config OO.org FAX device',
init_file_name=go_home('.padminrc'),
text=r"""[KnownFaxCommands]
0=sendfax -n -d (PHONE) (TMP)
""",
is_formatted=False,
constructor=ConfigFile,
).enact()
if when('look_and_feel'):
ChangeGConfKeyValue(
name='Show date on clock applet',
key='/apps/panel/applets/clock_screen0/prefs/show_date',
value=True,
).enact()
if IS_PANEL_DIRTY:
PROC = subprocess.Popen(
args='killall gnome-panel',
shell=True,
)
retcd = PROC.wait()
if IS_DESKTOP_DIRTY:
PROC = subprocess.Popen(
args='killall nautilus',
shell=True,
)
retcd = PROC.wait()
# Fin