summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Vogt <egon@top>2005-11-15 14:18:07 +0100
committerMichael Vogt <egon@top>2005-11-15 14:18:07 +0100
commitd632109cd5964f7d4baa408d517e44f801e1be8d (patch)
treec1acf5f14bcfa183951c260a52e5560610f3a988 /src
downloadpython-apt-d632109cd5964f7d4baa408d517e44f801e1be8d.tar.gz
* initial revision (after accidently killing it)
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am10
-rw-r--r--src/aptsources.py.in442
-rw-r--r--src/dialog_add.py.in107
-rw-r--r--src/dialog_apt_key.py.in163
-rw-r--r--src/dialog_edit.py.in102
-rw-r--r--src/dialog_settings.py.in144
-rwxr-xr-xsrc/gnome-software-properties.in332
-rw-r--r--src/update-manager.in874
-rw-r--r--src/utils.py12
9 files changed, 2186 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..62ea2e68
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,10 @@
+exedir = $(prefix)/bin
+
+exe_SCRIPTS = update-manager
+
+properties_modules_DATA = utils.py
+properties_modulesdir = $(datadir)/update-manager/python
+
+
+EXTRA_DIST = update-manager.in \
+ utils.py
diff --git a/src/aptsources.py.in b/src/aptsources.py.in
new file mode 100644
index 00000000..7badd141
--- /dev/null
+++ b/src/aptsources.py.in
@@ -0,0 +1,442 @@
+# aptsource.py.in - parse sources.list
+#
+# Copyright (c) 2004 Canonical
+# 2004 Michiel Sikkes
+#
+# Author: Michiel Sikkes <michiel@eyesopened.nl>
+# Michael Vogt <mvo@debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import string
+import gettext
+import re
+
+# actual source.list entries
+class SourceEntry:
+
+ # works mostely like split but takes [] into account
+ def mysplit(self, line):
+ line = string.strip(line)
+ pieces = []
+ tmp = ""
+ # we are inside a [..] block
+ p_found = False
+ space_found = False
+ for i in range(len(line)):
+ if line[i] == "[":
+ p_found=True
+ tmp += line[i]
+ elif line[i] == "]":
+ p_found=False
+ tmp += line[i]
+ elif space_found and not line[i].isspace(): # we skip one or more space
+ space_found = False
+ pieces.append(tmp)
+ tmp = line[i]
+ elif line[i].isspace() and not p_found: # found a whitespace
+ space_found = True
+ else:
+ tmp += line[i]
+ # append last piece
+ if len(tmp) > 0:
+ pieces.append(tmp)
+ return pieces
+
+
+ # parse a given source line and split it into the fields we need
+ def parse(self,line):
+ line = string.strip(self.line)
+ #print line
+ # check if the source is enabled/disabled
+ if line == "" or line == "#":
+ self.invalid = True
+ return
+ if line[0] == "#":
+ self.disabled = True
+ pieces = string.split(line[1:])
+ # if it looks not like a disabled deb line return
+ if not (pieces[0] == "deb" or pieces[0] == "deb-src"):
+ self.invalid = True
+ return
+ else:
+ line = line[1:]
+ # check for another "#" in the line (this is treated as a comment)
+ i = line.find("#")
+ if i > 0:
+ self.comment = line[i+1:]
+ line = line[:i]
+ # source is ok, split it and see what we have
+ pieces = self.mysplit(line)
+ # Type, deb or deb-src
+ self.type = string.strip(pieces[0])
+ # URI
+ self.uri = string.strip(pieces[1])
+ # distro and components (optional)
+ # Directory or distro
+ self.dist = string.strip(pieces[2])
+ if len(pieces) > 3:
+ # List of components
+ self.comps = pieces[3:]
+ else:
+ self.comps = []
+
+ #print self.__dict__
+
+
+ # set enabled/disabled
+ def set_enabled(self, new_value):
+ self.disabled = not new_value
+ # enable, remove all "#" from the start of the line
+ if new_value == True:
+ i=0
+ self.line = string.lstrip(self.line)
+ while self.line[i] == "#":
+ i += 1
+ self.line = self.line[i:]
+ else:
+ # disabled, add a "#"
+ if string.strip(self.line)[0] != "#":
+ self.line = "#" + self.line
+
+
+ def __init__(self, line):
+ self.invalid = False
+ self.disabled = False
+ self.type = ""
+ self.uri = ""
+ self.dist = ""
+ self.comps = []
+ self.comment = ""
+ self.line = line
+ self.parse(line)
+
+
+ def str(self):
+ return self.line
+
+
+def uniq(s):
+ """ simple (and not efficient) way to return uniq list """
+ u = []
+ for x in s:
+ if x not in u:
+ u.append(x)
+ return u
+
+
+# the SourceList file as a class
+class SourcesList:
+ def __init__(self, file):
+ self.list = [] # of Type SourceEntries
+ self.load(file)
+
+ def is_mirror(self, add_uri, orig_uri):
+ """check if the given add_url is idential or a mirror of orig_uri
+ e.g. add_uri = archive.ubuntu.com
+ orig_uri = de.archive.ubuntu.com
+ -> True
+ """
+ # remove traling spaces and "/"
+ add_uri = add_uri.rstrip("/ ")
+ orig_uri = orig_uri.rstrip("/ ")
+ # uri is identical
+ if add_uri == orig_uri:
+ #print "Identical"
+ return True
+ # add uri is a master site and orig_uri has the from "XX.mastersite"
+ # (e.g. de.archive.ubuntu.com)
+ try:
+ add_srv = add_uri.split("//")[1]
+ orig_srv = orig_uri.split("//")[1]
+ #print "%s == %s " % (add_srv, orig_srv)
+ except IndexError: # ok, somethings wrong here
+ #print "IndexError"
+ return False
+ if add_srv == orig_srv[3:]:
+ #print "Mirror"
+ return True
+ return False
+
+ def add(self, type, uri, dist, comps, comment="", pos=-1):
+ # if there is a repo with the same (type, uri, dist) just add the
+ # components
+ for i in self.list:
+ if i.type == type and self.is_mirror(uri,i.uri) and i.dist == dist:
+ comps = uniq(i.comps + comps)
+ # preserver mirror
+ uri = i.uri
+ # set to the old position and preserve comment
+ comment = i.comment
+ pos = self.list.index(i)
+ self.list.remove(i)
+ line = "%s %s %s" % (type,uri,dist)
+ for c in comps:
+ line = line + " " + c;
+ if comment != "":
+ line = "%s #%s\n" %(line,comment)
+ line = line + "\n"
+ self.list.insert(pos, SourceEntry(line))
+
+ def remove(self, source_entry):
+ self.list.remove(source_entry)
+
+ def load(self,file):
+ f = open(file, "r")
+ lines = f.readlines()
+ for line in lines:
+ source = SourceEntry(line)
+ self.list.append(source)
+ f.close()
+
+ def save(self,file):
+ f=open(file,"w")
+ for source in self.list:
+ f.write(source.str())
+ f.close()
+
+
+# templates for the add dialog
+class SourceEntryTemplate(SourceEntry):
+ def __init__(self,a_type,uri,dist,description,comps):
+ self.comps = []
+ self.comps_descriptions = []
+ self.type = a_type
+ self.uri = uri
+ self.dist = dist
+ self.description = description
+ self.comps = comps
+
+class SourceCompTemplate:
+ def __init__(self, name, description, on_by_default):
+ self.name = name
+ self.description = description
+ self.on_by_default = on_by_default
+
+class SourceEntryTemplates:
+ def __init__(self):
+ _ = gettext.gettext
+ self.templates = []
+
+ # ubuntu components templates
+ ubuntu_comps = []
+ ubuntu_comps.append(SourceCompTemplate("main",_("Officially supported"),True))
+ ubuntu_comps.append(SourceCompTemplate("restricted",_("Restricted copyright"),True))
+ ubuntu_comps.append(SourceCompTemplate("universe",_("Community maintained (Universe)"),False))
+ ubuntu_comps.append(SourceCompTemplate("multiverse",_("Non-free (Multiverse)"),False))
+
+ # ubuntu distro
+ self.templates.append(SourceEntryTemplate("deb",
+ "http://archive.ubuntu.com/ubuntu/",
+ "breezy",
+ "Ubuntu 5.10 \"Breezy Badger\"",
+ ubuntu_comps ))
+ self.templates.append(SourceEntryTemplate("deb",
+ "http://security.ubuntu.com/ubuntu/",
+ "breezy-security",
+ _("Ubuntu 5.10 Security Updates"),
+ ubuntu_comps))
+ self.templates.append(SourceEntryTemplate("deb",
+ "http://archive.ubuntu.com/ubuntu/",
+ "breezy-updates",
+ _("Ubuntu 5.10 Updates"),
+ ubuntu_comps))
+
+
+# matcher class to make a source entry look nice
+# lots of predefined matchers to make it i18n/gettext friendly
+class SourceEntryMatcher:
+ class MatchType:
+ def __init__(self, a_type,a_descr):
+ self.type = a_type
+ self.description = a_descr
+
+ class MatchDist:
+ def __init__(self,a_uri,a_dist, a_descr,l_comps, l_comps_descr):
+ self.uri = a_uri
+ self.dist = a_dist
+ self.description = a_descr
+ self.comps = l_comps
+ self.comps_descriptions = l_comps_descr
+
+ def __init__(self):
+ _ = gettext.gettext
+ self.type_list = []
+ self.type_list.append(self.MatchType("^deb$",_("Binary")))
+ self.type_list.append(self.MatchType("^deb-src$",_("Source")))
+
+ self.dist_list = []
+
+ ubuntu_comps = ["^main$","^restricted$","^universe$","^multiverse$"]
+ ubuntu_comps_descr = [_("Officially supported"),
+ _("Restricted copyright"),
+ _("Community maintained (Universe)"),
+ _("Non-free (Multiverse)")]
+ # CDs
+ self.dist_list.append(self.MatchDist("cdrom:\[Ubuntu.*4.10",
+ ".*",
+ _("CD") +
+ " Ubuntu 4.10 \"Warty Warthog\"",
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist("cdrom:\[Ubuntu.*5.04",
+ ".*",
+ _("CD") +
+ " Ubuntu 5.04 \"Hoary Hedgehog\"",
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist("cdrom:\[Ubuntu.*5.10",
+ ".*",
+ _("CD") +
+ " Ubuntu 5.10 \"Breezy Badger\"",
+ ubuntu_comps, ubuntu_comps_descr))
+
+ # URIs
+ # normal archive
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^warty$",
+ "Ubuntu 4.10 \"Warty Warthog\"",
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^hoary$",
+ "Ubuntu 5.04 \"Hoary Hedgehog\"",
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^breezy$",
+ "Ubuntu 5.10 \"Breezy Badger\"",
+ ubuntu_comps, ubuntu_comps_descr))
+ # updates
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^hoary-updates$",
+ _("Ubuntu 5.04 Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^breezy-updates$",
+ _("Ubuntu 5.10 Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+
+ # security
+ self.dist_list.append(self.MatchDist(".*security.ubuntu.com/ubuntu",
+ "^warty-security$",
+ _("Ubuntu 4.10 Security Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist(".*security.ubuntu.com/ubuntu",
+ "^hoary-security$",
+ _("Ubuntu 5.04 Security Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist(".*security.ubuntu.com/ubuntu",
+ "^breezy-security$",
+ _("Ubuntu 5.10 Security Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+ # security (normal archive uri)
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^warty-security$",
+ _("Ubuntu 4.10 Security Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^hoary-security$",
+ _("Ubuntu 5.04 Security Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+ self.dist_list.append(self.MatchDist(".*archive.ubuntu.com/ubuntu",
+ "^breezy-security$",
+ _("Ubuntu 5.10 Security Updates"),
+ ubuntu_comps, ubuntu_comps_descr))
+
+
+ # DEBIAN
+ debian_comps = ["^main$","^contrib$","^non-free$","^non-US$"]
+ debian_comps_descr = [_("Officially supported"),
+ _("Contributed software"),
+ _("Non-free software"),
+ _("US export restricted software")
+ ]
+
+ # dists by name
+ self.dist_list.append(self.MatchDist(".*debian.org/debian",
+ "^sarge$",
+ "Debian 3.1 \"Sarge\"",
+ debian_comps, debian_comps_descr))
+ self.dist_list.append(self.MatchDist(".*debian.org/debian",
+ "^woody$",
+ "Debian 3.0 \"Woody\"",
+ debian_comps, debian_comps_descr))
+ # securtiy
+ self.dist_list.append(self.MatchDist(".*security.debian.org",
+ "^stable.*$",
+ _("Debian Stable Security Updates"),
+ debian_comps, debian_comps_descr))
+ # dists by status
+ self.dist_list.append(self.MatchDist(".*debian.org/debian",
+ "^stable$",
+ "Debian Stable",
+ debian_comps, debian_comps_descr))
+ self.dist_list.append(self.MatchDist(".*debian.org/debian",
+ "^testing$",
+ "Debian Testing",
+ debian_comps, debian_comps_descr))
+ self.dist_list.append(self.MatchDist(".*debian.org/debian",
+ "^unstable$",
+ "Debian Unstable \"Sid\"",
+ debian_comps, debian_comps_descr))
+
+ # non-us
+ self.dist_list.append(self.MatchDist(".*debian.org/debian-non-US",
+ "^stable.*$",
+ "Debian Non-US (Stable)",
+ debian_comps, debian_comps_descr))
+ self.dist_list.append(self.MatchDist(".*debian.org/debian-non-US",
+ "^testing.*$",
+ "Debian Non-US (Testing)",
+ debian_comps, debian_comps_descr))
+ self.dist_list.append(self.MatchDist(".*debian.org/debian-non-US",
+ "^unstable.*$",
+ "Debian Non-US (Unstable)",
+ debian_comps, debian_comps_descr))
+
+
+
+
+ def match(self,source):
+ _ = gettext.gettext
+ # some sane defaults first
+ type_description = source.type
+ dist_description = source.uri + " " + source.dist
+ comp_description = ""
+ for c in source.comps:
+ comp_description = comp_description + " " + c
+
+ for t in self.type_list:
+ if re.match(t.type, source.type):
+ type_description = _(t.description)
+ break
+
+ for d in self.dist_list:
+ #print "'%s'" %source.uri
+ if re.match(d.uri, source.uri) and re.match(d.dist,source.dist):
+ dist_description = d.description
+ comp_description = ""
+ for c in source.comps:
+ found = False
+ for i in range(len(d.comps)):
+ if re.match(d.comps[i], c):
+ comp_description = comp_description+"\n"+d.comps_descriptions[i]
+ found = True
+ if found == False:
+ comp_description = comp_description+" "+c
+ break
+
+
+ return (type_description,dist_description,comp_description)
+
+
diff --git a/src/dialog_add.py.in b/src/dialog_add.py.in
new file mode 100644
index 00000000..141fc009
--- /dev/null
+++ b/src/dialog_add.py.in
@@ -0,0 +1,107 @@
+# dialog_add.py.in - dialog to add a new repository
+#
+# Copyright (c) 2004 Canonical
+#
+# Author: Michael Vogt <mvo@debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import os
+import gobject
+import gtk
+import gtk.glade
+
+import aptsources
+
+class dialog_add:
+ def __init__(self,parent,sourceslist):
+ self.sourceslist = sourceslist
+
+ # templates
+ self.templatelist = aptsources.SourceEntryTemplates()
+
+ # gtk stuff
+ if os.path.exists("../data/gnome-software-properties.glade"):
+ self.gladexml = gtk.glade.XML("../data/gnome-software-properties.glade")
+ else:
+ self.gladexml = gtk.glade.XML("@prefix@/share/update-manager/gnome-software-properties.glade")
+
+ self.main = widget = self.gladexml.get_widget("dialog_add")
+ self.main.set_transient_for(parent)
+
+ combo = self.gladexml.get_widget("combobox_what")
+ self.gladexml.signal_connect("on_combobox_what_changed", self.on_combobox_what_changed, None)
+ # combox box needs
+ cell = gtk.CellRendererText()
+ combo.pack_start(cell, True)
+ combo.add_attribute(cell, 'text', 0)
+ self.fill_combo(combo)
+ self.gladexml.signal_connect("on_button_custom_clicked",
+ self.on_button_custom_clicked, None)
+
+
+ def fill_combo(self,combo):
+ liststore = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT)
+ for item in self.templatelist.templates:
+ liststore.append((item.description, item))
+ combo.set_model(liststore)
+ combo.set_active(0)
+
+ def on_combobox_what_changed(self, combobox, user):
+ #print "on_combobox_what_changed"
+ vbox = self.gladexml.get_widget("vbox_comps")
+ vbox.foreach(lambda widget,vbox: vbox.remove(widget), vbox)
+ liststore = combobox.get_model()
+ a_iter = liststore.iter_nth_child(None, combobox.get_active())
+ (name, template) = liststore.get(a_iter, 0,1)
+ self.selected = template
+ comps = template.comps
+ for c in comps:
+ checkbox = gtk.CheckButton(c.description)
+ checkbox.set_active(c.on_by_default)
+ checkbox.set_data("name",c.name)
+ vbox.pack_start(checkbox)
+ checkbox.show()
+
+ def on_button_custom_clicked(self, widget, data):
+ #print "on_button_custom_clicked()"
+ # this hide here is ugly :/
+ self.main.hide()
+ dialog = self.gladexml.get_widget("dialog_add_custom")
+ res = dialog.run()
+ dialog.hide()
+ entry = self.gladexml.get_widget("entry_source_line")
+ line = entry.get_text() + "\n"
+ self.sourceslist.list.append(aptsources.SourceEntry(line))
+ self.main.response(res)
+
+ def get_enabled_comps(self, checkbutton):
+ if checkbutton.get_active():
+ self.selected_comps.append(checkbutton.get_data("name"))
+
+ def run(self):
+ res = self.main.run()
+ if res == gtk.RESPONSE_OK:
+ # add repository
+ self.selected_comps = []
+ vbox = self.gladexml.get_widget("vbox_comps")
+ vbox.foreach(self.get_enabled_comps)
+ self.sourceslist.add(self.selected.type,
+ self.selected.uri,
+ self.selected.dist,
+ self.selected_comps)
+ self.main.hide()
+ return res
diff --git a/src/dialog_apt_key.py.in b/src/dialog_apt_key.py.in
new file mode 100644
index 00000000..d11dfd0a
--- /dev/null
+++ b/src/dialog_apt_key.py.in
@@ -0,0 +1,163 @@
+# dialog_apt_key.py.in - edit the apt keys
+#
+# Copyright (c) 2004 Canonical
+#
+# Author: Michael Vogt <mvo@debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import os
+import gobject
+import gtk
+import gtk.glade
+import subprocess
+import gettext
+from utils import error
+from subprocess import PIPE
+
+# gettext convenient
+_ = gettext.gettext
+def dummy(e): return e
+N_ = dummy
+
+# some known keys
+N_("Ubuntu Archive Automatic Signing Key <ftpmaster@ubuntu.com>")
+N_("Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>")
+
+class apt_key:
+ def __init__(self):
+ self.gpg = ["/usr/bin/gpg"]
+ self.base_opt = self.gpg + ["--no-options", "--no-default-keyring",
+ "--secret-keyring", "/etc/apt/secring.gpg",
+ "--trustdb-name", "/etc/apt/trustdb.gpg",
+ "--keyring", "/etc/apt/trusted.gpg"]
+ self.list_opt = self.base_opt + ["--with-colons", "--batch",
+ "--list-keys"]
+ self.rm_opt = self.base_opt + ["--quiet", "--batch",
+ "--delete-key", "--yes"]
+ self.add_opt = self.base_opt + ["--quiet", "--batch",
+ "--import"]
+
+
+ def list(self):
+ res = []
+ #print self.list_opt
+ p = subprocess.Popen(self.list_opt,stdout=PIPE).stdout
+ for line in p.readlines():
+ fields = line.split(":")
+ if fields[0] == "pub":
+ name = fields[9]
+ res.append("%s %s\n%s" %((fields[4])[-8:],fields[5], _(name)))
+ return res
+
+ def add(self, filename):
+ #print "request to add " + filename
+ cmd = self.add_opt[:]
+ cmd.append(filename)
+ p = subprocess.Popen(cmd)
+ return (p.wait() == 0)
+
+ def update(self):
+ cmd = ["/usr/bin/apt-key", "update"]
+ p = subprocess.Popen(cmd)
+ return (p.wait() == 0)
+
+ def rm(self, key):
+ #print "request to remove " + key
+ cmd = self.rm_opt[:]
+ cmd.append(key)
+ p = subprocess.Popen(cmd)
+ return (p.wait() == 0)
+
+class dialog_apt_key:
+ def __init__(self, parent):
+ # gtk stuff
+ if os.path.exists("../data/gnome-software-properties.glade"):
+ self.gladexml = gtk.glade.XML("../data/gnome-software-properties.glade")
+ else:
+ self.gladexml = gtk.glade.XML("@prefix@/share/update-manager/gnome-software-properties.glade")
+ self.main = self.gladexml.get_widget("dialog_apt_key")
+ self.main.set_transient_for(parent)
+
+ self.gladexml.signal_connect("on_button_key_add_clicked",
+ self.on_button_key_add_clicked)
+ self.gladexml.signal_connect("on_button_key_remove_clicked",
+ self.on_button_key_remove_clicked)
+ self.gladexml.signal_connect("on_button_apt_key_update_clicked",
+ self.on_button_apt_key_update_clicked)
+
+ # create apt-key object (abstraction for the apt-key command)
+ self.apt_key = apt_key()
+
+ # get some widgets
+ self.treeview_apt_key = self.gladexml.get_widget("treeview_apt_key")
+ self.liststore_apt_key = gtk.ListStore(str)
+ self.treeview_apt_key.set_model(self.liststore_apt_key)
+ # Create columns and append them.
+ tr = gtk.CellRendererText()
+ tr.set_property("xpad", 10)
+ tr.set_property("ypad", 10)
+ c0 = gtk.TreeViewColumn("Key", tr, text=0)
+ self.treeview_apt_key.append_column(c0)
+ self.update_key_list()
+
+ def on_button_apt_key_update_clicked(self, widget):
+ self.apt_key.update()
+ self.update_key_list()
+
+ def on_button_key_add_clicked(self, widget):
+ chooser = gtk.FileChooserDialog(title=_("Choose a key-file"),
+ parent=self.main,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_REJECT,
+ gtk.STOCK_OK,gtk.RESPONSE_ACCEPT))
+ res = chooser.run()
+ chooser.hide()
+ if res == gtk.RESPONSE_ACCEPT:
+ #print chooser.get_filename()
+ if not self.apt_key.add(chooser.get_filename()):
+ error(self.main,
+ _("Error importing selected file"),
+ _("The selected file may not be a GPG key file "
+ "or it might be corrupt."))
+ self.update_key_list()
+
+ def on_button_key_remove_clicked(self, widget):
+ selection = self.treeview_apt_key.get_selection()
+ (model,a_iter) = selection.get_selected()
+ if a_iter == None:
+ return
+ key = model.get_value(a_iter,0)
+ if not self.apt_key.rm(key[:8]):
+ error(self.main,
+ _("Error removing the key"),
+ _("The key you selected could not be removed. "
+ "Please report this as a bug."))
+ self.update_key_list()
+
+ def update_key_list(self):
+ self.liststore_apt_key.clear()
+ for key in self.apt_key.list():
+ self.liststore_apt_key.append([key])
+
+ def run(self):
+ res = self.main.run()
+ self.main.hide()
+
+
+if __name__ == "__main__":
+ ui = dialog_apt_key(None)
+ ui.run()
+
diff --git a/src/dialog_edit.py.in b/src/dialog_edit.py.in
new file mode 100644
index 00000000..02eac6c0
--- /dev/null
+++ b/src/dialog_edit.py.in
@@ -0,0 +1,102 @@
+# dialog_edit.py.in - edit a existing repository
+#
+# Copyright (c) 2004 Canonical
+#
+# Author: Michael Vogt <mvo@debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import os
+import gobject
+import gtk
+import gtk.glade
+
+import aptsources
+
+class dialog_edit:
+ def __init__(self,parent,sourceslist,source_entry):
+ self.sourceslist = sourceslist
+ self.source_entry = source_entry
+
+ # gtk stuff
+ if os.path.exists("../data/gnome-software-properties.glade"):
+ self.gladexml = gtk.glade.XML("../data/gnome-software-properties.glade")
+ else:
+ self.gladexml = gtk.glade.XML("@prefix@/share/update-manager/gnome-software-properties.glade")
+ self.main = self.gladexml.get_widget("dialog_edit")
+ self.main.set_transient_for(parent)
+
+ # type
+ combo_type = self.gladexml.get_widget("combobox_type")
+ if source_entry.type == "deb":
+ combo_type.set_active(0)
+ elif source_entry.type == "deb-src":
+ combo_type.set_active(1)
+ else:
+ print "Error, unknown source type: '%s'" % source_enrty.type
+
+ # uri
+ entry = self.gladexml.get_widget("entry_uri")
+ entry.set_text(source_entry.uri)
+
+ entry = self.gladexml.get_widget("entry_dist")
+ entry.set_text(source_entry.dist)
+
+ entry = self.gladexml.get_widget("entry_comps")
+ comps = ""
+ for c in source_entry.comps:
+ if len(comps) > 0:
+ comps = comps + " " + c
+ else:
+ comps = c
+ entry.set_text(comps)
+
+ entry = self.gladexml.get_widget("entry_comment")
+ entry.set_text(source_entry.comment)
+
+ def run(self):
+ res = self.main.run()
+ if res == gtk.RESPONSE_OK:
+ # get values
+ combo_type = self.gladexml.get_widget("combobox_type")
+ if combo_type.get_active() == 0:
+ line = "deb"
+ else:
+ line = "deb-src"
+ entry = self.gladexml.get_widget("entry_uri")
+ line = line + " " + entry.get_text()
+
+ entry = self.gladexml.get_widget("entry_dist")
+ line = line + " " + entry.get_text()
+
+ entry = self.gladexml.get_widget("entry_comps")
+ line = line + " " + entry.get_text()
+
+ entry = self.gladexml.get_widget("entry_comment")
+ if entry.get_text() != "":
+ line = line + " #" + entry.get_text() + "\n"
+ else:
+ line = line + "\n"
+
+ # change repository
+ index = self.sourceslist.list.index(self.source_entry)
+ self.sourceslist.list[index] = aptsources.SourceEntry(line)
+ #self.sourceslist.add(self.selected.type,
+ # self.selected.uri,
+ # self.selected.dist,
+ # self.selected_comps)
+ self.main.hide()
+ return res
diff --git a/src/dialog_settings.py.in b/src/dialog_settings.py.in
new file mode 100644
index 00000000..cfa13e38
--- /dev/null
+++ b/src/dialog_settings.py.in
@@ -0,0 +1,144 @@
+# dialog_settings.py.in - edit some settings
+#
+# Copyright (c) 2005 Canonical
+#
+# Author: Michael Vogt <mvo@debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import gconf
+import apt_pkg
+import gtk
+import os
+
+periodicAptConfFile = "/etc/apt/apt.conf.d/10periodic"
+archiveAptConfFile = "/etc/apt/apt.conf.d/20archive"
+
+class dialog_settings:
+ def save_periodic_config(self):
+ #print "saving ..."
+
+ # get the new values
+ for key in self.conf_map:
+ cb = self.gladexml.get_widget("checkbutton_%s"% key)
+ sb = self.gladexml.get_widget("spinbutton_%s"% key)
+ if cb and not cb.get_active():
+ #print "%s=%s" % (self.conf_map[key], "0")
+ apt_pkg.Config.Set(self.conf_map[key], "0")
+ elif sb:
+ value = sb.get_value()
+ apt_pkg.Config.Set(self.conf_map[key], str(value))
+ #print "%s=%s" % (self.conf_map[key], value)
+
+ # special case for autodownload, it has the same interval as
+ # Update-Package-Lists
+ cb = self.gladexml.get_widget("checkbutton_autodownload")
+ key = "autodownload"
+ if cb.get_active():
+ autoupdate = str(apt_pkg.Config.FindI("APT::Periodic::Update-Package-Lists"))
+ apt_pkg.Config.Set(self.conf_map[key], autoupdate)
+ else:
+ apt_pkg.Config.Set(self.conf_map[key], "0")
+
+
+ # write both config-prefixes to different files
+ for (file, prefix) in ((periodicAptConfFile, "APT::Periodic"),
+ (archiveAptConfFile, "APT::Archives")):
+
+ content = []
+ if os.path.isfile(file):
+ content=open(file,"r").readlines()
+
+ cnf = apt_pkg.Config.SubTree(prefix)
+
+ f = open(file,"w+")
+ for line in content:
+ # don't write the udpated keys
+ found = False
+ for key in cnf.List():
+ #print "%s=%s" % (key, cnf[key])
+ if line.find("%s::%s" % (prefix,key)) >= 0:
+ found = True
+ break
+ if not found:
+ f.write(line)
+ # write new keys
+ for i in cnf.List():
+ f.write("%s::%s \"%s\";\n" % (prefix,i,cnf.FindI((i))))
+ f.close()
+
+ def toggle_show_disabled(self, widget, data):
+ self.show_disabled = widget.get_active()
+ self.gconfclient.set_bool("/apps/gnome-software-properties/show_disabled",\
+ self.show_disabled)
+
+ def toggle_settings_cb(self, widget, data):
+ mode = widget.get_active()
+ self.gladexml.get_widget(data).set_sensitive(mode)
+
+ def run(self):
+ res = self.main_window.run()
+ self.save_periodic_config()
+ self.main_window.hide()
+ return res
+
+ def __init__(self, parent, glade):
+
+ self.gladexml = glade
+ self.main_window = self.gladexml.get_widget("dialog_settings")
+ self.main_window.set_transient_for(parent)
+ self.parent = parent
+ self.gconfclient = gconf.client_get_default()
+
+ # preferences entries
+ self.show_disabled = self.gconfclient.get_bool("/apps/gnome-software-properties/show_disabled")
+
+ checkbutton_show_disabled = self.gladexml.get_widget("checkbutton_show_disabled")
+ checkbutton_show_disabled.set_active(self.show_disabled)
+ checkbutton_show_disabled.connect("toggled", self.toggle_show_disabled, None)
+
+
+ # apt-config
+
+ # set the update stuff
+ self.conf_map = {
+ "autoupdate" : "APT::Periodic::Update-Package-Lists",
+ "autodownload" : "APT::Periodic::Download-Upgradeable-Packages",
+ "autoclean" : "APT::Periodic::AutocleanInterval",
+ "max_size" : "APT::Archives::MaxSize",
+ "max_age" : "APT::Archives::MaxAge"
+ }
+
+ for key in self.conf_map:
+ value = apt_pkg.Config.FindI(self.conf_map[key])
+ #print "%s=%s" % (key, value)
+ cb = self.gladexml.get_widget("checkbutton_%s"% key)
+ #if cb == None:
+ # print "checkbutton_%s not found" % key
+ sb = self.gladexml.get_widget("spinbutton_%s"% key)
+ if sb != None:
+ #print "setting %s to %s" % (key, value)
+ sb.set_value(value)
+ #else:
+ # print "spinbutton_%s not found" % key
+ box = self.gladexml.get_widget("vbox_%s"% key)
+ #if box == None:
+ # print "vbox_%s not found" % key
+ if box and cb:
+ cb.connect("toggled", self.toggle_settings_cb, ("vbox_%s" % key))
+ if cb:
+ cb.set_active(value)
+
diff --git a/src/gnome-software-properties.in b/src/gnome-software-properties.in
new file mode 100755
index 00000000..6c9b8d3b
--- /dev/null
+++ b/src/gnome-software-properties.in
@@ -0,0 +1,332 @@
+#!/usr/bin/python2.4
+# gnome-software-properties.in - edit /etc/apt/sources.list
+#
+# Copyright (c) 2004 Canonical
+# 2004 Michiel Sikkes
+#
+# Author: Michiel Sikkes <michiel@eyesopened.nl>
+# Michael Vogt <mvo@debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import pygtk
+import sys
+pygtk.require('2.0')
+import gtk
+import gtk.glade
+import gconf
+import gobject
+import os
+import gettext
+import re
+import string
+import time
+import tempfile
+import subprocess
+from optparse import OptionParser
+import apt_pkg
+
+sys.path.append("@prefix@/share/update-manager/python")
+
+import aptsources
+import dialog_add
+import dialog_edit
+import dialog_apt_key
+from dialog_settings import dialog_settings
+import shutil
+
+
+(LIST_MARKUP, LIST_ENABLED, LIST_ENTRY_OBJ) = range(3)
+
+class SoftwareConfigurator:
+
+ def on_button_ok_clicked(self, widget, data):
+ #self.save_periodic_config(periodicAptConfFile)
+
+ location = "/etc/apt/sources.list"
+ #backup first
+ shutil.copy(location,location+".save")
+ # write
+ self.sourceslist.save(location)
+
+ # write the source.list first, even if dirty=False, because
+ # e.g. CD-ROM add does not set dirty variable even if it adds
+ # something to the sources.list (but no reload needed)
+ if self.dirty == False:
+ gtk.main_quit()
+ sys.exit(0)
+
+ primary = "<span weight=\"bold\" size=\"larger\">" + _("Repositories "
+ "changed") + "</span>"
+ secondary = _("The repository information has changes. A backup copy of "
+ "your sources.list is stored in %s.save. "
+ "\n\n"
+ "You need to reload the package list from the servers "
+ "for your changes to take effect. Do you want to do this "
+ "now?") % location
+ dialog = gtk.MessageDialog(self.main_window,gtk.DIALOG_MODAL,
+ gtk.MESSAGE_INFO,gtk.BUTTONS_YES_NO,"")
+ dialog.set_markup(primary);
+ dialog.format_secondary_text(secondary);
+ #textview = gtk.TextView()
+ #textview.set_editable(gtk.FALSE)
+ #textbuffer = textview.get_buffer()
+ #f = os.popen("/usr/bin/diff -u %s.save %s" % (location,location))
+ #diff = f.read()
+ #textbuffer.set_text(diff)
+ #if f.close() != None:
+ # win = gtk.ScrolledWindow()
+ # win.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+ # win.add(textview)
+ # win.set_size_request(400,300)
+ # win.show()
+ # dialog.vbox.pack_start(child = win, padding = 6)
+ # textview.show()
+ res = dialog.run()
+ dialog.destroy()
+ if res == gtk.RESPONSE_YES:
+ self.main_window.hide()
+ # we are in "no-update" mode, this is used when the calling application
+ # wants to deal with this itself
+ if options.no_update:
+ gtk.main_quit()
+ sys.exit(1)
+ child = subprocess.Popen(["/usr/sbin/synaptic", "--update-at-startup",
+ "--hide-main-window","--non-interactive"],
+ close_fds=True)
+ # wait for the child to finish
+ while child.poll() == None:
+ time.sleep(0.05)
+ while gtk.events_pending():
+ gtk.main_iteration()
+ gtk.main_quit()
+ sys.exit(1)
+ gtk.main_quit()
+ sys.exit(0)
+
+ def on_button_edit_clicked(self, widget, data):
+ selection = self.sourceslist_view.get_selection()
+ (model, iter) = selection.get_selected()
+ source_entry = model.get_value(iter, LIST_ENTRY_OBJ)
+ edit = dialog_edit.dialog_edit(self.main_window,self.sourceslist,
+ source_entry)
+ if edit.run() == gtk.RESPONSE_OK:
+ self.reloadsources(self.sourceslist,self.matcher)
+ self.dirty = True
+
+ def on_sourceslist_selection_changed(self, selection, data):
+ (model, iter) = selection.get_selected()
+ if iter != None:
+ self.button_del.set_sensitive(True)
+ self.button_edit.set_sensitive(True)
+ else:
+ self.button_del.set_sensitive(False)
+ self.button_edit.set_sensitive(False)
+
+ #def on_sourceslist_row_activated(self, treeview, path, column, data):
+ # #print "on_row_activated()"
+ # model = treeview.get_model()
+ # source_entry = model.get_value(model.get_iter(path), LIST_ENTRY_OBJ)
+ # edit = dialog_edit.dialog_edit(self.main_window,self.sourceslist,
+ # source_entry)
+ # edit.run()
+ # self.reloadsources(self.sourceslist,self.matcher)
+
+ def on_button_remove_clicked(self, widget, data):
+ selection = self.sourceslist_view.get_selection()
+ (model,a_iter) = selection.get_selected()
+ if a_iter == None:
+ return
+ source = model.get_value(a_iter, LIST_ENTRY_OBJ)
+ self.sourceslist.remove(source)
+ self.reloadsources(self.sourceslist,self.matcher)
+ self.dirty=True
+
+ def on_button_add_clicked(self, widget, data):
+ add = dialog_add.dialog_add(self.main_window,self.sourceslist)
+ if add.run() == gtk.RESPONSE_OK:
+ self.reloadsources(self.sourceslist,self.matcher)
+ self.dirty=True
+
+ def on_button_settings_clicked(self, widget, data):
+ settings = dialog_settings(self.main_window, self.gladexml)
+ settings.run()
+ self.show_disabled = self.gconfclient.get_bool("/apps/gnome-software-properties/show_disabled")
+ self.c_enabled.set_property("visible", self.show_disabled)
+ self.reloadsources(self.sourceslist, self.matcher)
+
+ def on_button_add_cdrom_clicked(self, widget, data):
+ tmp = tempfile.NamedTemporaryFile()
+ cmd = ["/usr/sbin/synaptic", "--hide-main-window", "--non-interactive",
+ "-o","Dir::Etc::sourcelist=%s" % tmp.name,"--ask-cdrom" ]
+ self.main_window.set_sensitive(False)
+ proc = subprocess.Popen(cmd)
+ # wait for process to finish
+ while proc.poll() == None:
+ while gtk.events_pending():
+ gtk.main_iteration()
+ time.sleep(0.05)
+ self.main_window.set_sensitive(True)
+ # read tmp file with source name
+ line = ""
+ for x in open(tmp.name):
+ line = x
+ if line != "":
+ self.sourceslist.list.append(aptsources.SourceEntry(line))
+ self.reloadsources(self.sourceslist,self.matcher)
+ #self.dirty=True # no need here
+
+ def on_button_authentication_clicked(self, widget, data):
+ auth = dialog_apt_key.dialog_apt_key(self.main_window)
+ auth.run()
+
+ def reloadsources(self, sourceslist, matcher):
+ #self.meta = meta_data()
+ self.sourcesstore.clear()
+ for source in sourceslist.list:
+ if source.invalid or (source.disabled and not self.show_disabled):
+ continue
+ (a_type,dist,comps) = matcher.match(source)
+ contents = ""
+ if source.comment != "":
+ contents += "<i>%s</i>\n\n" % (source.comment)
+ contents +="<big><b>%s </b></big> (%s) <small>\n%s</small>" % (dist,a_type, comps)
+ iter = self.sourcesstore.append([contents, not source.disabled, source])
+
+ def show_help(self, widget, data):
+ print "self.show_help() called"
+
+ # toggled on/off a source in the listview
+ def toggled_enabled(self, renderer, path_string):
+ iter = self.sourcesstore.get_iter_from_string(path_string)
+ source = self.sourcesstore.get_value(iter, LIST_ENTRY_OBJ)
+ self.dirty=True
+ if self.sourcesstore.get_value(iter, LIST_ENABLED):
+ self.sourcesstore.set_value(iter, LIST_ENABLED, False)
+ source.set_enabled(False)
+ else:
+ self.sourcesstore.set_value(iter, LIST_ENABLED, True)
+ source.set_enabled(True)
+
+ def __init__(self):
+ self.gconfclient = gconf.client_get_default()
+
+ if os.path.exists("../data/gnome-software-properties.glade"):
+ self.gladexml = gtk.glade.XML("../data/gnome-software-properties.glade")
+ else:
+ self.gladexml = gtk.glade.XML("@prefix@/share/update-manager/gnome-software-properties.glade")
+
+ # do we show disabled sources?
+ self.show_disabled = self.gconfclient.get_bool("/apps/gnome-software-properties/show_disabled")
+ self.main_window = self.gladexml.get_widget("SoftwareConfigurator")
+ # button on the right
+ self.gladexml.signal_connect("on_button_edit_clicked",
+ self.on_button_edit_clicked, None)
+ self.button_edit = self.gladexml.get_widget("button_edit")
+ self.button_del = self.gladexml.get_widget("button_remove")
+
+ # Gets the treeview and creates a store for it.
+ self.sourceslist_view = self.gladexml.get_widget("sourceslist")
+ self.sourcesstore = gtk.ListStore(str, bool,gobject.TYPE_PYOBJECT)
+ self.sourceslist_view.set_model(self.sourcesstore)
+ #self.gladexml.signal_connect("on_sourceslist_row_activated",
+ # self.on_sourceslist_row_activated, None)
+ self.sourceslist_view.get_selection().connect("changed", self.on_sourceslist_selection_changed, None)
+
+ # was something modified
+ self.dirty=False
+
+ # Create columns and append them.
+ cr = gtk.CellRendererToggle()
+ cr.set_property("activatable", True)
+ cr.set_property("xpad", 10)
+ cr.set_property("ypad", 10)
+ cr.connect("toggled", self.toggled_enabled)
+ self.c_enabled = gtk.TreeViewColumn("Enabled", cr, active=LIST_ENABLED)
+ self.sourceslist_view.append_column(self.c_enabled)
+ self.c_enabled.set_property("visible", self.show_disabled)
+
+ tr = gtk.CellRendererText()
+ tr.set_property("xpad", 10)
+ tr.set_property("ypad", 10)
+ c0 = gtk.TreeViewColumn("Entry", tr, markup=LIST_MARKUP)
+ self.sourceslist_view.append_column(c0)
+
+ self.sourceslist = aptsources.SourcesList("@sysconfdir@/apt/sources.list")
+ self.matcher = aptsources.SourceEntryMatcher()
+ # Empty and fill the sources store.
+ self.reloadsources(self.sourceslist,self.matcher)
+
+ self.main_window = self.gladexml.get_widget("SoftwareConfigurator")
+ self.main_window.connect("delete_event", lambda widget,ev: gtk.main_quit())
+
+ okbutton = self.gladexml.get_widget("button_ok")
+ okbutton.connect("clicked", self.on_button_ok_clicked, None)
+
+ cancelbutton = self.gladexml.get_widget("button_cancel")
+ cancelbutton.connect("clicked", lambda w,v: gtk.main_quit(), None)
+
+ self.gladexml.signal_connect("on_button_add_clicked", self.on_button_add_clicked, None)
+ self.gladexml.signal_connect("on_button_add_cdrom_clicked", self.on_button_add_cdrom_clicked, None)
+ self.gladexml.signal_connect("on_button_remove_clicked", self.on_button_remove_clicked, None)
+ self.gladexml.signal_connect("on_button_authentication_clicked", self.on_button_authentication_clicked, None)
+
+ # settings
+ self.gladexml.signal_connect("on_button_settings_clicked", self.on_button_settings_clicked, None)
+
+ self.main_window.show()
+ if options.toplevel != None:
+ # don't show the add-cdrom button for now
+ # FIXME: on the long run interface with apt-pkg/cdrom.h
+ b = self.gladexml.get_widget("button_add_cdrom")
+ b.hide()
+ toplevel = gtk.gdk.window_foreign_new(int(options.toplevel))
+ self.main_window.window.set_transient_for(toplevel)
+
+
+ # init the config
+ apt_pkg.InitConfig()
+
+ def main(self):
+ gtk.main()
+
+
+if __name__ == "__main__":
+ APP="update-manager"
+ DIR="@prefix@/share/locale"
+ gettext.bindtextdomain(APP, DIR)
+ gettext.textdomain(APP)
+ gtk.glade.bindtextdomain(APP, DIR)
+ gtk.glade.textdomain(APP)
+ _ = gettext.gettext
+ #print "Software Configurator started..."
+
+ # add option parser
+ parser = OptionParser()
+ parser.add_option("-n", "--no-update", action="store_true",
+ dest="no_update", default=False,
+ help="No update on repository change (usefull if called "\
+ "from a external program).")
+ parser.add_option("-t", "--toplevel",
+ action="store", type="string", dest="toplevel",
+ help="Set x-window-id of the toplevel parent for the "\
+ "dialog (usefull for embedding)")
+
+
+ (options, args) = parser.parse_args()
+ ui = SoftwareConfigurator()
+ ui.main()
+ #print "Software Configurator ended..."
diff --git a/src/update-manager.in b/src/update-manager.in
new file mode 100644
index 00000000..90928b8e
--- /dev/null
+++ b/src/update-manager.in
@@ -0,0 +1,874 @@
+#!/usr/bin/python2.4
+# update-manager.in - easy updating application
+#
+# Copyright (c) 2004 Canonical
+# 2004 Michiel Sikkes
+#
+# Author: Michiel Sikkes <michiel@eyesopened.nl>
+# Michael Vogt <mvo@debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gtk.gdk
+import gtk.glade
+import gobject
+import gnome
+import apt_pkg
+import gettext
+import copy
+import string
+import sys
+import os
+import os.path
+import urllib2
+import re
+import thread
+import tempfile
+import time
+import rfc822
+import gconf
+import pango
+import subprocess
+import pwd
+import xml.sax.saxutils
+
+
+# FIXME:
+# - cary a reference to the update-class around in the ListStore
+# - kill "all_changes" and move the changes into the "Update" class
+
+# list constants
+(LIST_INSTALL, LIST_CONTENTS, LIST_NAME, LIST_SHORTDESC,
+ LIST_VERSION, LIST_LONG_DESCR, LIST_PKG) = range(7)
+
+# actions for "invoke_manager"
+(INSTALL, UPDATE) = range(2)
+
+SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences"
+
+METARELEASE_URI = "http://changelogs.ubuntu.com/meta-release"
+#METARELEASE_URI = "http://people.ubuntu.com/~mvo/meta-release-test"
+METARELEASE_FILE = "/var/lib/update-manager/meta-release"
+
+CHANGELOGS_URI="http://changelogs.ubuntu.com/changelogs/pool/%s/%s/%s/%s_%s/changelog"
+
+# fixme: use a utils package for this sort of stuff
+def str_to_bool(str):
+ if str == "0" or str.upper() == "FALSE":
+ return False
+ return True
+
+def utf8(str):
+ return unicode(str, 'latin1').encode('utf-8')
+
+class Update:
+
+ def __init__(self, package, cache, records, depcache):
+ #package = cache[name]
+ name = package.Name
+ version = depcache.GetCandidateVer(package)
+ file, index = version.FileList.pop(0)
+ records.Lookup((file, index))
+
+ self.name = name
+ self.version = version.VerStr
+ self.shortdesc = records.ShortDesc
+ self.longdesc = ""
+ self.size = version.Size
+
+ longdesc = records.LongDesc
+ lines = longdesc.split("\n")
+ lines.pop(0)
+ for line in lines:
+ line = line[1:]
+ first_char = string.strip(line)[0]
+ if line == ".":
+ self.longdesc = self.longdesc + "\n"
+ else:
+ self.longdesc = self.longdesc + line + "\n"
+
+class UpdateList:
+ def __init__(self, parent_window):
+ self.pkgs = []
+ self.num_updates = 0
+ self.parent_window = parent_window
+
+ def saveDistUpgrade(self, cache, depcache):
+ """ this functions mimics a upgrade but will never remove anything """
+ depcache.Upgrade(True)
+ if depcache.DelCount > 0:
+ # nice try, falling back
+ for pkg in cache.Packages:
+ depcache.MarkKeep(pkg)
+ assert depcache.BrokenCount == 0 and depcache.DelCount == 0
+ depcache.Upgrade()
+
+ def update(self, cache, records, depcache):
+ held_back = []
+ broken = []
+ self.saveDistUpgrade(cache, depcache)
+ for pkg in cache.Packages:
+ if depcache.MarkedUpgrade(pkg) or depcache.MarkedInstall(pkg):
+ self.pkgs.append(Update(pkg, cache, records, depcache))
+ self.num_updates = self.num_updates + 1
+ elif depcache.IsInstBroken(pkg) or depcache.IsNowBroken(pkg):
+ broken.append(pkg.Name)
+ elif pkg.CurrentVer != None and depcache.IsUpgradable(pkg):
+ #print "MarkedKeep: %s " % pkg.Name
+ held_back.append(pkg.Name)
+ self.pkgs.sort(lambda x,y: cmp(x.name,y.name))
+ if depcache.BrokenCount > 0:
+ # FIXME: show what packages are broken
+ msg=("<big><b>%s</b></big>\n\n%s"%(_("Your system has broken packages!"),
+ _("This means that some dependencies "
+ "of the installed packages are not "
+ "satisfied. Please use \"Synaptic\" "
+ "or \"apt-get\" to fix the "
+ "situation."
+ )))
+ dialog = gtk.MessageDialog(self.parent_window, 0, gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_OK,"")
+ dialog.set_markup(msg)
+ dialog.vbox.set_spacing(6)
+ dialog.run()
+ dialog.destroy()
+ sys.exit(1)
+ if depcache.KeepCount > 0:
+ #print "WARNING, keeping packages"
+ msg=("<big><b>%s</b></big>\n\n%s"%(_("It is not possible to upgrade "
+ "all packages."),
+ _("This means that "
+ "besides the actual upgrade of the "
+ "packages some further action "
+ "(such as installing or removing "
+ "packages) "
+ "is required. Please use Synaptic "
+ "\"Smart Upgrade\" or "
+ "\"apt-get dist-upgrade\" to fix "
+ "the situation."
+ )))
+ dialog = gtk.MessageDialog(self.parent_window, 0, gtk.MESSAGE_INFO,
+ gtk.BUTTONS_OK,"")
+ dialog.set_default_response(gtk.RESPONSE_OK)
+ dialog.set_markup(msg)
+ dialog.vbox.set_spacing(6)
+ label = gtk.Label(_("The following packages are not upgraded: "))
+ label.set_alignment(0.0,0.5)
+ dialog.set_border_width(6)
+ label.show()
+ dialog.vbox.pack_start(label)
+ scroll = gtk.ScrolledWindow()
+ scroll.set_size_request(-1,200)
+ scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ text = gtk.TextView()
+ text.set_editable(False)
+ text.set_cursor_visible(False)
+ buf = text.get_buffer()
+ held_back.sort()
+ buf.set_text("\n".join(held_back))
+ scroll.add(text)
+ dialog.vbox.pack_start(scroll)
+ scroll.show_all()
+ dialog.run()
+ dialog.destroy()
+
+
+class UpdateManager:
+
+ # FIXME: wrong location for this func
+ # don't touch the gui in this function, it needs to be thread-safe
+ def get_changelog(self, name, lock):
+ pkg = self.cache[name]
+
+ # FIXME: not correct, need to get canidateVer
+ version = self.depcache.GetCandidateVer(pkg)
+ file, index = version.FileList.pop(0)
+ self.records.Lookup((file, index))
+ if self.records.SourcePkg != "":
+ srcpkg = self.records.SourcePkg
+ else:
+ srcpkg = name
+
+ src_section = "main"
+ l = string.split(pkg.Section,"/")
+ if len(l) > 1:
+ sec_section = l[0]
+
+ prefix = srcpkg[0]
+ if srcpkg.startswith("lib"):
+ prefix = "lib" + srcpkg[3]
+
+ verstr = version.VerStr
+ l = string.split(verstr,":")
+ if len(l) > 1:
+ verstr = l[1]
+
+ try:
+ uri = CHANGELOGS_URI % (src_section,prefix,srcpkg,srcpkg, verstr)
+ changelog = urllib2.urlopen(uri)
+ #print changelog.read()
+ # do only get the lines that are new
+ alllines = ""
+ regexp = "^%s \((.*)\)(.*)$" % (srcpkg)
+
+ i=0
+ while True:
+ line = changelog.readline()
+ #print line
+ if line == "":
+ break
+ match = re.match(regexp,line)
+ if match:
+ if apt_pkg.VersionCompare(match.group(1),pkg.CurrentVer.VerStr) <= 0:
+ break
+ # EOF (shouldn't really happen)
+ alllines = alllines + line
+
+ # only write if we where not canceld
+ if lock.locked():
+ self.all_changes[name] = [alllines, srcpkg]
+ except urllib2.HTTPError:
+ if lock.locked():
+ self.all_changes[name] = [_("Changes not found, the server may not be updated yet."), srcpkg]
+ except IOError:
+ if lock.locked():
+ self.all_changes[name] = [_("Failed to download changes. Please check if there is an active internet connection."), srcpkg]
+ if lock.locked():
+ lock.release()
+
+ def set_changes_buffer(self, changes_buffer, text, name, srcpkg):
+ changes_buffer.set_text("")
+ lines = text.split("\n")
+ if len(lines) == 1:
+ changes_buffer.set_text(text)
+ return
+
+ for line in lines:
+
+ end_iter = changes_buffer.get_end_iter()
+
+ version_match = re.match("^%s \((.*)\)(.*)$" % (srcpkg), line)
+ #bullet_match = re.match("^.*[\*-]", line)
+ author_match = re.match("^.*--.*<.*@.*>.*$", line)
+ if version_match:
+ version = version_match.group(1)
+ version_text = _("Version %s: \n") % version
+ changes_buffer.insert_with_tags_by_name(end_iter, version_text, "versiontag")
+ # mvo: disabled for now as it does not catch multi line entries
+ # (see ubuntu #7034 for rational)
+ #elif bullet_match and not author_match:
+ # bullet_text = " " + line + "\n"
+ # changes_buffer.insert(end_iter, bullet_text)
+ elif (author_match):
+ pass
+ #chanages_buffer.insert(end_iter, "\n")
+ else:
+ changes_buffer.insert(end_iter, line+"\n")
+
+
+ def cursor_changed(self, widget):
+ tuple = widget.get_cursor()
+ path = tuple[0]
+ # check if we have a path at all
+ if path == None:
+ return
+ model = widget.get_model()
+ iter = model.get_iter(path)
+
+ # set descr
+ long_desc = model.get_value(iter, 5)
+ if long_desc == None:
+ return
+ desc_buffer = self.DescView.get_buffer()
+ desc_buffer.set_text(utf8(long_desc))
+
+ # now do the changelog
+ name = model.get_value(iter, 2)
+ if name == None:
+ return
+
+ changes_buffer = self.ChangesView.get_buffer()
+
+ # check if we have the changes already
+ if self.all_changes.has_key(name):
+ changes = self.all_changes[name]
+ self.set_changes_buffer(changes_buffer, changes[0], name, changes[1])
+ else:
+ if self.expander.get_expanded():
+ self.treeview.set_sensitive(False)
+ self.Glade.get_widget("hbox_footer").set_sensitive(False)
+ lock = thread.allocate_lock()
+ lock.acquire()
+ t=thread.start_new_thread(self.get_changelog,(name,lock))
+ changes_buffer.set_text(_("Downloading changes..."))
+ button = self.Glade.get_widget("button_cancel_dl_changelog")
+ button.show()
+ id = button.connect("clicked",
+ lambda w,lock: lock.release(), lock)
+ # wait for the dl-thread
+ while lock.locked():
+ time.sleep(0.05)
+ while gtk.events_pending():
+ gtk.main_iteration()
+ # download finished (or canceld, or time-out)
+ button.hide()
+ button.disconnect(id);
+ self.treeview.set_sensitive(True)
+ self.Glade.get_widget("hbox_footer").set_sensitive(True)
+
+ if self.all_changes.has_key(name):
+ changes = self.all_changes[name]
+ self.set_changes_buffer(changes_buffer, changes[0], name, changes[1])
+
+ def remove_update(self, pkg):
+ name = pkg.name
+ if name in self.packages:
+ self.packages.remove(name)
+ self.dl_size -= pkg.size
+ if len(self.packages) == 0:
+ self.installbutton.set_sensitive(False)
+ self.update_count()
+
+ def add_update(self, pkg):
+ name = pkg.name
+ if name not in self.packages:
+ self.packages.append(name)
+ self.dl_size += pkg.size
+ if len(self.packages) > 0:
+ self.installbutton.set_sensitive(True)
+ self.update_count()
+
+ def update_count(self):
+ text = "%i (%s)" % (len(self.packages),
+ apt_pkg.SizeToStr(self.dl_size))
+ self.NumUpdates.set_text(text)
+
+ def activate_details(self, expander, data):
+ expanded = self.expander.get_expanded()
+ self.gconfclient.set_bool("/apps/update-manager/show_details",expanded)
+ if expanded:
+ self.cursor_changed(self.treeview)
+
+ def run_synaptic(self, id, action, lock):
+ apt_pkg.PkgSystemUnLock()
+ cmd = ["/usr/sbin/synaptic", "--hide-main-window", "--non-interactive",
+ "--plug-progress-into", "%s" % (id) ]
+ if action == INSTALL:
+ cmd.append("--set-selections")
+ cmd.append("--progress-str")
+ cmd.append("%s" % _("The updates are being applied."))
+ cmd.append("--finish-str")
+ cmd.append("%s" % _("Upgrade finished"))
+ proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
+ f = proc.stdin
+ for s in self.packages:
+ f.write("%s\tinstall\n" % s)
+ f.close()
+ proc.wait()
+ elif action == UPDATE:
+ cmd.append("--update-at-startup")
+ subprocess.call(cmd)
+ else:
+ print "run_synaptic() called with unknown action"
+ sys.exit(1)
+
+ # use this once gksudo does propper reporting
+ #if os.geteuid() != 0:
+ # if os.system("gksudo /bin/true") != 0:
+ # return
+ # cmd = "sudo " + cmd;
+ lock.release()
+
+ def plug_removed(self, w, (win,socket)):
+ #print "plug_removed"
+ # plug was removed, but we don't want to get it removed, only hiden
+ # unti we get more
+ win.hide()
+ return True
+
+ def plug_added(self, sock, win):
+ win.show()
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ def on_button_reload_clicked(self, widget):
+ #print "on_button_reload_clicked"
+ self.invoke_manager(UPDATE)
+
+ def on_button_help_clicked(self, widget):
+ gnome.help_display_desktop(self.gnome_program, "update-manager", "update-manager", "")
+
+ def on_button_install_clicked(self, widget):
+ #print "on_button_install_clicked"
+ self.invoke_manager(INSTALL)
+
+ def invoke_manager(self, action):
+ # check first if no other package manager is runing
+ import struct, fcntl
+ lock = os.path.dirname(apt_pkg.Config.Find("Dir::State::status"))+"/lock"
+ lock_file= open(lock)
+ flk=struct.pack('hhllhl',fcntl.F_WRLCK,0,0,0,0,0)
+ try:
+ rv = fcntl.fcntl(lock_file, fcntl.F_GETLK, flk)
+ except IOError:
+ print "Error getting lockstatus"
+ raise
+ locked = struct.unpack('hhllhl', rv)[0]
+ if locked != fcntl.F_UNLCK:
+ msg=("<big><b>%s</b></big>\n\n%s"%(_("Another package manager is "
+ "running"),
+ _("You can run only one "
+ "package management application "
+ "at the same time. Please close "
+ "this other application first.")));
+ dialog = gtk.MessageDialog(self.main_window, 0, gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_OK,"")
+ dialog.set_markup(msg)
+ dialog.run()
+ dialog.destroy()
+ return
+
+ # don't display apt-listchanges, we already showed the changelog
+ os.environ["APT_LISTCHANGES_FRONTEND"]="none"
+
+ # set window to insensitive
+ self.main_window.set_sensitive(False)
+ # create a progress window that will swallow the synaptic progress bars
+ win = gtk.Window()
+ if action==UPDATE:
+ win.set_title(_("Updating package list..."))
+ else:
+ win.set_title(_("Installing updates..."))
+ win.set_border_width(6)
+ win.set_transient_for(self.main_window)
+ win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
+ win.resize(400,200)
+ win.set_resizable(False)
+ # prevent the window from closing with the delete button (there is
+ # a cancel button in the window)
+ win.connect("delete_event", lambda e,w: True);
+
+ # create the socket
+ socket = gtk.Socket()
+ socket.show()
+ win.add(socket)
+
+ socket.connect("plug-added", self.plug_added, win)
+ socket.connect("plug-removed", self.plug_removed, (win,socket))
+ lock = thread.allocate_lock()
+ lock.acquire()
+ t = thread.start_new_thread(self.run_synaptic,(socket.get_id(),action,lock))
+ while lock.locked():
+ while gtk.events_pending():
+ gtk.main_iteration()
+ time.sleep(0.05)
+ win.destroy()
+ while gtk.events_pending():
+ gtk.main_iteration()
+ self.fillstore()
+ self.main_window.set_sensitive(True)
+
+ def toggled(self, renderer, path_string):
+ """ a toggle button in the listview was toggled """
+ iter = self.store.get_iter_from_string(path_string)
+ if self.store.get_value(iter, LIST_INSTALL):
+ self.store.set_value(iter, LIST_INSTALL, False)
+ self.remove_update(self.store.get_value(iter, LIST_PKG))
+ else:
+ self.store.set_value(iter, LIST_INSTALL, True)
+ self.add_update(self.store.get_value(iter, LIST_PKG))
+
+
+ def exit(self):
+ """ exit the application, save the state """
+ self.save_state()
+ gtk.main_quit()
+ sys.exit(0)
+
+ def save_state(self):
+ """ save the state (window-size for now) """
+ (x,y) = self.main_window.get_size()
+ self.gconfclient.set_pair("/apps/update-manager/window_size",
+ gconf.VALUE_INT, gconf.VALUE_INT, x, y)
+
+ def restore_state(self):
+ """ restore the state (window-size for now) """
+ expanded = self.gconfclient.get_bool("/apps/update-manager/show_details")
+ self.expander.set_expanded(expanded)
+ (x,y) = self.gconfclient.get_pair("/apps/update-manager/window_size",
+ gconf.VALUE_INT, gconf.VALUE_INT)
+ if x > 0 and y > 0:
+ self.main_window.resize(x,y)
+
+ def on_button_preferences_clicked(self, widget):
+ """ start gnome-software preferences """
+ # args: "-n" means we take care of the reloading of the
+ # package list ourself
+ apt_pkg.PkgSystemUnLock()
+ args = ['/usr/bin/gnome-software-properties', '-n']
+ child = subprocess.Popen(args)
+ self.main_window.set_sensitive(False)
+ res = None
+ while res == None:
+ res = child.poll()
+ time.sleep(0.05)
+ while gtk.events_pending():
+ gtk.main_iteration()
+ # repository information changed, call "reload"
+ try:
+ apt_pkg.PkgSystemLock()
+ except SystemError:
+ print "Error geting the cache"
+ apt_pkg.PkgSystemLock()
+ if res > 0:
+ self.on_button_reload_clicked(None)
+ self.main_window.set_sensitive(True)
+
+ def __init__(self, download_changes_at_startup=False):
+
+ self.gnome_program = gnome.init("update-manager", "0.39")
+
+ self.download_changes_at_startup = download_changes_at_startup
+ self.packages = []
+ self.dl_size = 0
+ self.all_changes = {}
+ self.dist = self.get_dist()
+ if os.path.exists("../data/update-manager.glade"):
+ self.Glade = gtk.glade.XML("../data/update-manager.glade")
+ else:
+ self.Glade = gtk.glade.XML("@prefix@/share/update-manager/update-manager.glade")
+
+ self.NumUpdates = self.Glade.get_widget("num_updates")
+ self.main_window = self.Glade.get_widget("MainWindow")
+ self.main_window.connect("delete_event", lambda w, ev: self.exit())
+ self.DescView = self.Glade.get_widget("descview")
+ self.ChangesView = self.Glade.get_widget("textview_changes")
+ changes_buffer = self.ChangesView.get_buffer()
+ changes_buffer.create_tag("versiontag", weight=pango.WEIGHT_BOLD)
+ self.expander = self.Glade.get_widget("expander_details")
+ self.expander.connect("notify::expanded", self.activate_details)
+
+ self.installbutton = self.Glade.get_widget("button_install")
+ self.Glade.signal_connect("on_button_install_clicked",
+ self.on_button_install_clicked)
+ self.Glade.signal_connect("on_button_close_clicked",
+ lambda w: self.exit())
+ self.Glade.signal_connect("on_button_reload_clicked",
+ self.on_button_reload_clicked)
+ self.Glade.signal_connect("on_button_preferences_clicked",
+ self.on_button_preferences_clicked)
+ self.Glade.signal_connect("on_button_help_clicked",
+ self.on_button_help_clicked)
+
+ self.treeview = self.Glade.get_widget("updatelist")
+
+ self.store = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str, str, str, str,
+ gobject.TYPE_PYOBJECT)
+ self.treeview.set_model(self.store)
+ self.treeview.set_headers_clickable(True);
+
+ self.treeview.connect('cursor-changed', self.cursor_changed)
+
+ tr = gtk.CellRendererText()
+ tr.set_property("xpad", 10)
+ tr.set_property("ypad", 10)
+ cr = gtk.CellRendererToggle()
+ cr.set_property("activatable", True)
+ cr.set_property("xpad", 10)
+ cr.connect("toggled", self.toggled)
+ self.cb = gtk.TreeViewColumn("Install", cr, active=LIST_INSTALL)
+ c0 = gtk.TreeViewColumn("Name", tr, markup=LIST_CONTENTS)
+ c0.set_resizable(True)
+ major,minor,patch = gtk.pygtk_version
+ if (major >= 2) and (minor >= 5):
+ self.cb.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ self.cb.set_fixed_width(30)
+ c0.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ c0.set_fixed_width(100)
+ #self.treeview.set_fixed_height_mode(True)
+
+ self.treeview.append_column(self.cb)
+ self.cb.set_visible(False);
+ self.treeview.append_column(c0)
+ self.treeview.set_search_column(LIST_NAME)
+ #self.treeview.append_column(c1)
+ #self.treeview.append_column(c2)
+ #self.treeview.set_headers_visible(False)
+ # set expander to last position
+
+ # proxy stuff
+ SYNAPTIC_CONF_FILE = "%s/.synaptic/synaptic.conf" % pwd.getpwuid(0)[5]
+ if os.path.exists(SYNAPTIC_CONF_FILE):
+ cnf = apt_pkg.newConfiguration()
+ apt_pkg.ReadConfigFile(cnf, SYNAPTIC_CONF_FILE)
+ use_proxy = cnf.FindB("Synaptic::useProxy", False)
+ if use_proxy:
+ proxy_host = cnf.Find("Synaptic::httpProxy")
+ proxy_port = str(cnf.FindI("Synaptic::httpProxyPort"))
+ if proxy_host and proxy_port:
+ proxy_support = urllib2.ProxyHandler({"http":"http://%s:%s" % (proxy_host, proxy_port)})
+ opener = urllib2.build_opener(proxy_support)
+ urllib2.install_opener(opener)
+
+ self.gconfclient = gconf.client_get_default()
+ # restore state
+ self.restore_state()
+
+ def fillstore(self):
+ if self.download_changes_at_startup:
+ dialog = self.Glade.get_widget("dialog_fetching")
+ dialog.set_transient_for(self.main_window)
+ dialog.set_modal(True)
+ progress = self.Glade.get_widget("progressbar_fetching")
+ dialog.show()
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ # clean most objects
+ self.packages = []
+ self.dl_size = 0
+ self.all_changes = {}
+ self.store.clear()
+ self.initCache()
+ self.list = UpdateList(self.main_window)
+
+ # fill them again
+ self.list.update(self.cache, self.records, self.depcache)
+ if self.list.num_updates < 1:
+ # set the label and treeview and hide the checkbox column
+ self.cb.set_visible(False)
+ self.expander.hide()
+ label = self.Glade.get_widget("label_header")
+ text = "<big><b>%s</b></big>\n\n%s" % (_("Your system is up-to-date!"),
+ _("There are no updates available."))
+ label.set_markup(text)
+ self.store.append([False, _("Your system is up-to-date!"), None, None, None, None, None])
+ # make sure no install is possible
+ self.installbutton.set_sensitive(False)
+ else:
+ self.cb.set_visible(True)
+ self.expander.show()
+ self.treeview.set_headers_visible(False)
+ label = self.Glade.get_widget("label_header")
+ text = _("<big><b>Available Updates</b></big>\n"
+ "\n"
+ "The following packages are found to be upgradable. You can upgrade them by "
+ "using the Install button.")
+ label.set_markup(text)
+ i=0
+ for pkg in self.list.pkgs:
+ if self.download_changes_at_startup:
+ progress.set_fraction(float(i)/len(self.list.pkgs))
+ while gtk.events_pending():
+ gtk.main_iteration()
+ lock = thread.allocate_lock()
+ self.all_changes[pkg.name] = self.get_changelog(pkg.name,lock)
+
+ name = xml.sax.saxutils.escape(pkg.name)
+ summary = xml.sax.saxutils.escape(pkg.shortdesc)
+ contents = "<big><b>%s</b></big>\n<small>%s\n\n" % (name, summary)
+ contents = contents + _("New version: %s") % (pkg.version) + "</small>"
+
+ iter = self.store.append([True, contents, pkg.name, pkg.shortdesc, pkg.version, pkg.longdesc, pkg])
+ self.add_update(pkg)
+ i = i + 1
+
+ if self.download_changes_at_startup:
+ dialog.hide()
+
+ self.update_count()
+ return False
+
+ # FIXME: use lsb-release binary and cache the result
+ def get_dist(self):
+ f = open("/etc/lsb-release", "r")
+ lines = f.readlines()
+ for line in lines:
+ key, value = line.split("=")
+ if (key == "DISTRIB_CODENAME"):
+ return value[:-1]
+ f.close()
+
+ def current_dist_not_supported(self, name):
+ #print name
+ msg = "<big><b>%s</b></big>\n\n%s" % (_("Your distribution is no longer supported"), _("Please upgrade to a newer version of Ubuntu Linux. The version you are running will no longer get security fixes or other critical updates. Please see http://www.ubuntulinux.org for upgrade information."))
+ dialog = gtk.MessageDialog(self.main_window, 0, gtk.MESSAGE_WARNING,
+ gtk.BUTTONS_OK,"")
+ dialog.set_markup(msg)
+ dialog.run()
+ dialog.destroy()
+
+
+ def new_dist_available(self, name):
+ #print name
+ # check if the user already knowns about this dist
+ seen = self.gconfclient.get_string("/apps/update-manager/seen_dist")
+ if name == seen:
+ return
+
+ msg = "<big><b>%s</b></big>\n\n%s" % (_("There is a new release of Ubuntu available!"), _("A new release with the codename '%s' is available. Please see http://www.ubuntulinux.org/ for upgrade instructions.") % name)
+ dialog = gtk.MessageDialog(self.main_window, 0, gtk.MESSAGE_INFO,
+ gtk.BUTTONS_CLOSE, "")
+ dialog.set_markup(msg)
+ check = gtk.CheckButton(_("Never show this message again"))
+ check.show()
+ dialog.vbox.pack_start(check)
+ dialog.run()
+ if check.get_active():
+ self.gconfclient.set_string("/apps/update-manager/seen_dist",name)
+ dialog.destroy()
+
+ # code that does the meta release file checking
+ def check_meta_release(self):
+ #print "check_meta_release"
+ current_dist = self.dist
+ dists = {}
+ if self.metarelease_information != None:
+ #print "meta_release found (current_dist: %s)" % (current_dist)
+ # we have a meta-release file
+ current_dist_date = 0
+ current_dist_supported = False
+ new_dist_available = False
+ # parse it
+ index_tag = apt_pkg.ParseTagFile(self.metarelease_information)
+ step_result = index_tag.Step()
+ while step_result:
+ if index_tag.Section.has_key("Dist"):
+ dist = index_tag.Section["Dist"]
+ date = time.mktime(rfc822.parsedate(index_tag.Section["Date"]))
+ dists[dist] = date
+ if dist == current_dist:
+ current_dist_supported = str_to_bool(index_tag.Section["Supported"])
+ current_dist_date = time.mktime(rfc822.parsedate(index_tag.Section["Date"]))
+ step_result = index_tag.Step()
+ # check for newer dists
+ new_dist = ""
+ found = False
+ for dist in dists:
+ if dist == current_dist:
+ found = True
+ if dists[dist] > current_dist_date and not dist == current_dist:
+ new_dist = dist
+ current_dist_date = dists[dist]
+
+ # we know nothing about the installed distro, so we just return
+ # silently
+ if not found:
+ return False
+
+ # only warn if unsupported and a new dist is available (because
+ # the development version is also unsupported)
+ if new_dist != "" and not current_dist_supported:
+ self.current_dist_not_supported(new_dist)
+ elif new_dist != "":
+ self.new_dist_available(new_dist)
+ # don't run this event again
+ return False
+ # we have no information about the meta-release, so run it again
+ return True
+
+ # the network thread that tries to fetch the meta-index file
+ def get_meta_release(self):
+ lastmodified = 0
+ req = urllib2.Request(METARELEASE_URI)
+ if os.access(METARELEASE_FILE, os.W_OK):
+ lastmodified = os.stat(METARELEASE_FILE).st_mtime
+ if lastmodified > 0:
+ req.add_header("If-Modified-Since", lastmodified)
+ try:
+ uri=urllib2.urlopen(req)
+ f=open(METARELEASE_FILE,"w+")
+ for line in uri.readlines():
+ f.write(line)
+ f.flush()
+ f.seek(0,0)
+ self.metarelease_information=f
+ uri.close()
+ except urllib2.URLError:
+ pass
+
+ # fixme: we should probably abstract away all the stuff from libapt
+ def initCache(self):
+ # get the lock
+ try:
+ apt_pkg.PkgSystemLock()
+ except SystemError:
+ d = gtk.MessageDialog(parent=self.main_window,
+ flags=gtk.DIALOG_MODAL,
+ type=gtk.MESSAGE_ERROR,
+ buttons=gtk.BUTTONS_OK)
+ d.set_markup("<big><b>%s</b></big>\n\n%s" % (
+ _("Unable to get exclusive lock"),
+ _("This usually means that another package management "
+ "application (like apt-get or aptitude) already running. "
+ "Please close that application first")))
+ res = d.run()
+ d.destroy()
+ sys.exit()
+
+ self.cache = apt_pkg.GetCache()
+ #apt_pkg.Config.Set("Debug::pkgPolicy","1")
+ self.depcache = apt_pkg.GetDepCache(self.cache)
+ self.depcache.ReadPinFile()
+ if os.path.exists(SYNAPTIC_PINFILE):
+ self.depcache.ReadPinFile(SYNAPTIC_PINFILE)
+ self.depcache.Init()
+ self.records = apt_pkg.GetPkgRecords(self.cache)
+
+
+ def main(self):
+ # FIXME: stat a check update thread
+ self.metarelease_information = None
+ t=thread.start_new_thread(self.get_meta_release, ())
+ gobject.timeout_add(1000, self.check_meta_release)
+ #self.get_meta_release()
+
+ self.store.append([True, _("Initializing and getting list of updates..."),
+ None, None, None, None, None])
+
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ # global init of apt, FIXME: move all the apt details in it's own class
+ apt_pkg.init()
+ self.fillstore()
+ gtk.main()
+
+
+if __name__ == "__main__":
+
+ APP="update-manager"
+ DIR="@prefix@/share/locale"
+ gettext.bindtextdomain(APP, DIR)
+ gettext.textdomain(APP)
+ gtk.glade.bindtextdomain(APP, DIR)
+ gtk.glade.textdomain(APP)
+ _ = gettext.gettext
+ if os.geteuid() != 0:
+ dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ _("You need to be root to run this program"))
+ dialog.run()
+ dialog.destroy()
+ sys.exit(1)
+
+ if (len(sys.argv) > 1) and (sys.argv[1].strip() == "--download-changes-at-startup"):
+ updatemanager = UpdateManager(True)
+ else:
+ updatemanager = UpdateManager()
+ updatemanager.main()
diff --git a/src/utils.py b/src/utils.py
new file mode 100644
index 00000000..3231ba98
--- /dev/null
+++ b/src/utils.py
@@ -0,0 +1,12 @@
+import gobject
+import gtk
+import gtk.glade
+
+def error(parent, primary, secondary):
+ p = "<span weight=\"bold\" size=\"larger\">%s</span>" % primary
+ dialog = gtk.MessageDialog(parent,gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR,gtk.BUTTONS_OK,"")
+ dialog.set_markup(p);
+ dialog.format_secondary_text(secondary);
+ dialog.run()
+ dialog.hide()