#!/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()
    self.Glade = gtk.glade.XML("/usr/share/update-manager/glade/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="/usr/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()
