sessioninstaller-0.20+bzr150/AUTHORS0000644000000000000000000000004611117523437015255 0ustar 00000000000000Sebastian Heinlein sessioninstaller-0.20+bzr150/COPYRIGHT0000644000000000000000000004325411377042146015511 0ustar 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. sessioninstaller-0.20+bzr150/HACKING0000644000000000000000000000021311365227571015175 0ustar 00000000000000To start the daemon from source run: python sessioninstaller/core.py. The test.py provides you a quick way to call the daemon via D-Bus. sessioninstaller-0.20+bzr150/NEWS0000644000000000000000000000214711413563627014714 0ustar 00000000000000VERSION 0.20 This release introduces the following features and changes: * Confirmation dialogs for installing (by name and file) and removing packages * Use the exe of the calling process instead of the parent's window title to identify the programme which has tiggered the action. Use the application name of an exectuable if a corresponding .desktop file is installed. Furthermore support python and perl scripts. * Support installing mime type handler (requires app-install-data to be installed) * Improved GStreamer plugin dialog text which takes the kind of the required GStreamer structures into account (e.g. ".. to play media files ...") * Use app-install-data and apt-xapian-index to show applications instead of packages in the confirmation dialog * Show a warning icon and set a tooltip for possibly restriceted packages in the confirmation dialog * Show additional details in the confirmation dialog (e.g. which GStreamer plugins are provided by the package) * Several bug fixes VERSION 0.10 The inital release of session-installer. See the README for more details. sessioninstaller-0.20+bzr150/README0000644000000000000000000000416311376164264015077 0ustar 00000000000000INTRODUCTION Sessioninstaller allows applications to easily install additional software (e.g. extensions or GSreamer codecs), to uninstall files and to perform simple software status queries by implementing the distribution neutral D-Bus session interface of PackageKit. The whole process of confirmation, error reporting and progress notification is handled by sessioninstaller. The reference implementation of the D-Bus interface can be found in gnome-packagekit. It was also adpoted by KPackageKit. In contrast to gnome-packagekit and KPackageKit it doesn't use the PackageKit daemon for querying and installation, but instead makes use of APT and aptdaemon (alternatively synaptic can be used). CONTACT The project is hosted at Launchpad: https://www.launchpad.net/sessioninstaller The source code is maintained in a bzr branch: bzr branch lp:sessioninstaller Bugs can be reported at the project home page. Feel free to contact me if you have got any questions, suggestions or comments. My email address is devel AT glatzor DOT de. STATUS The following list shows the implementation status of each interface method. * org.freedesktop.PackageKit.Query: - IsInstalled: Fully supported - SearchFile: Only allows to search for installed files by default, but can make use of apt-file to search for not installed files * org.freedesktop.PackageKit.Modify: - InstallPackageFile: Works with aptdaemon backend. Problem with handling multiple files. - InstallProvideFiles: Not supported. Could be implemented using apt-file. - InstallCatalogs: Only supports catalogs which use InstallPackages - InstallPackageNames: Fully supported. - InstallMimeTypes: Fully supported. Requires appinstall-data. - InstallFontConfigResources: Not supported. There in't any package font mapping available in Debian and Ubuntu. - InstallGStreamerResources: Fully supported. - InstallPrinterDrivers: Not supported. - RemovePackageByFiles: Fully supported. The interaction mode which allows to hide certain confirmations and dialogs is not supported. Currently sessioninstaller will always show all dialogs. sessioninstaller-0.20+bzr150/TODO0000644000000000000000000000032611364370045014675 0ustar 00000000000000NORMAL * ConfimInstallDialog: Allow to select between packages if the same codec is provided by more than one package * Add support for searching for mime type handlers (Keep an eye on software-center) sessioninstaller-0.20+bzr150/data/0000755000000000000000000000000011116327071015111 5ustar 00000000000000sessioninstaller-0.20+bzr150/doc/0000755000000000000000000000000011301022215014731 5ustar 00000000000000sessioninstaller-0.20+bzr150/gst-install0000755000000000000000000000200012561623432016364 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- """gstinstaller - install GStreamer components using PackageKit""" # Copyright (C) 2010 Sebastian Heinlein # # 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 # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. __author__ = "Sebastian Heinlein " from sessioninstaller.gstinstaller import main if __name__ == "__main__": main() # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/po/0000755000000000000000000000000011367007641014624 5ustar 00000000000000sessioninstaller-0.20+bzr150/release.sh0000755000000000000000000000064711367470616016202 0ustar 00000000000000#!/bin/bash VERSION=$1 if [ ! $(echo $VERSION | egrep "^[0-9\.]+$") ]; then echo "Specify a vaild version number as argument" exit 1 fi TARBALL=../tarballs/sessioninstaller-$VERSION.tar if [ -e $TARBALL.gz ]; then echo "Tarball already exists!" exit 1 fi bzr export --format tar $TARBALL bzr log --gnu-changelog -n0 > ChangeLog tar -rvf $TARBALL ChangeLog gzip $TARBALL gpg --armor --sign --detach-sig $TARBALL.gz sessioninstaller-0.20+bzr150/session-installer0000755000000000000000000000204712561623432017614 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- """sessioninstaller - APT based installer using PackgeKit's session DBus API""" # Copyright (C) 2008-2009 Sebastian Heinlein # # 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 # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. __author__ = "Sebastian Heinlein " __state__ = "experimental" from sessioninstaller.core import main if __name__ == "__main__": main() # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/0000755000000000000000000000000011117326366017610 5ustar 00000000000000sessioninstaller-0.20+bzr150/setup.py0000755000000000000000000000120711377143264015726 0ustar 00000000000000#!/usr/bin/env python import DistUtilsExtra.auto DistUtilsExtra.auto.setup(name="sessioninstaller", description="APT based installer using PackageKit session DBus API", homepage="http://launchpad.net/sessioninstaller", author="Sebastian Heinlein", packages=["sessioninstaller", "sessioninstaller.backends"], scripts=["session-installer", "gst-install"], data_files=[("share/dbus-1/services", ["data/sessioninstaller.service"]), ("share/man/man1", ["doc/session-installer.1", "doc/gst-install.1"])], license = "GNU GPL", platforms = "posix") sessioninstaller-0.20+bzr150/test0000755000000000000000000001763712561623432015130 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Test client for sessioninstaller""" # Copyright (C) 2008-2010 Sebastian Heinlein # # 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 # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. __author__ = "Sebastian Heinlein " __state__ = "experimental" import os from optparse import OptionParser from gettext import gettext as _ import dbus import dbus.mainloop.glib dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) from sessioninstaller.core import PACKAGEKIT_QUERY_DBUS_INTERFACE, \ PACKAGEKIT_MODIFY_DBUS_INTERFACE, \ PACKAGEKIT_DBUS_PATH, \ PACKAGEKIT_DBUS_SERVICE def main(): parser = OptionParser() parser.add_option("", "--install-packages", action="store", type="string", dest="install_packages", help=_("Install the given packages")) parser.add_option("", "--install-files", action="store", type="string", dest="install_files", help=_("Install package files")) parser.add_option("", "--install-provide-files", action="store", type="string", dest="install_provide", help=_("Install packages which provide the given files")) parser.add_option("", "--remove-files", action="store", type="string", dest="remove_files", help=_("Remove the packages which provide the given " "files")) parser.add_option("", "--is-installed", action="store", type="string", dest="is_installed", help=_("Check if a package is installed")) parser.add_option("", "--search-file", action="store", type="string", dest="search_file", help=_("Search for the package providing the given file")) parser.add_option("-i", "--interaction", action="store", type="string", dest="interaction", default="always", help=_("Specify the interaction mode by providing a " "comma spearated list of the following values: " "%s. This is currently not supported.") % ("show-confirm-search show-confirm-deps " "show-confirm-install show-progress " "show-finished show-warning")) parser.add_option("", "--install-catalog", action="store", type="string", dest="install_catalog", help=_("Install the packages specfied in the given " "PackageKit catalog")) parser.add_option("", "--install-mime-types", action="store", type="string", dest="install_mime_types", help=_("Install mime type handlers")) parser.add_option("", "--install-gstreamer", action="store", type="string", dest="install_gstreamer", help=_("Install the given GStreamer resource. The value " "'single' can be used as an alias for WMV9 and " "the value 'multi' as an alias for a group of " "codecs.")) parser.add_option("-t", "--timeout", action="store", type="int", default=300, help=_("Wait for the given seconds until the " "action is done. Defaults to 5 Minutes.")) options, args = parser.parse_args() xid = int(os.getenv("WINDOWID", "0")) bus = dbus.SessionBus() pk = bus.get_object(PACKAGEKIT_DBUS_SERVICE, PACKAGEKIT_DBUS_PATH, False) if options.remove_files: print(_("Removing files: %s") % options.remove_files) pk.RemovePackageByFiles(xid, options.remove_files.split(), options.interaction, timeout=options.timeout, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) elif options.is_installed: print(_("Checking if %s is installed") % options.is_installed) print(bool(pk.IsInstalled(options.is_installed, options.interaction, dbus_interface=PACKAGEKIT_QUERY_DBUS_INTERFACE))) elif options.search_file: print(_("Searching for %s") % options.search_file) print(pk.SearchFile(options.search_file, options.interaction, dbus_interface=PACKAGEKIT_QUERY_DBUS_INTERFACE)) elif options.install_catalog: print(_("Installing from catalog: %s") % options.install_catalog) pk.InstallCatalogs(xid, [options.install_catalog], options.interaction, timeout=options.timeout, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) elif options.install_files: print(_("Installing files: %s") % options.install_files) pk.InstallPackageFiles(xid, options.install_files.split(), options.interaction, timeout=options.timeout, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) elif options.install_packages: print(_("Installing packages: %s") % options.install_packages) pk.InstallPackageNames(xid, options.install_packages.split(), options.interaction, timeout=options.timeout, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) elif options.install_mime_types: print(_("Installing mimetype handlers: %s") % options.install_mime_types) pk.InstallMimeTypes(xid, options.install_mime_types.split(), options.interaction, timeout=options.timeout, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) elif options.install_provide: print(_("Installing providers: %s") % options.install_provide) pk.InstallProvideFiles(xid, options.install_provide.split(), options.interaction, timeout=options.timeout, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) elif options.install_gstreamer: if options.install_gstreamer == "single": resources = ["Windows Media Video 9|gstreamer0.10" "(decoder-video/x-wmv)"] elif options.install_gstreamer == "multi": resources = ["Windows Media Video 9|gstreamer0.10" "(decoder-video/x-wmv)", "H.264-Decoder|gstreamer0.10(decoder-video/x-h264)" "(profile=(string)baseline)(level=(string)1.1)", "MPeg 3|gstreamer0.10(decoder-audio/mpeg)" "(mpegversion=(int)1)(layer=(int)[ 1, 3 ])"] else: resources = [options.install_gstreamer] print(_("Installing GStreamer-Resource: %s") % resources) pk.InstallGStreamerResources(xid, resources, options.interaction, timeout=options.timeout, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) if __name__ == "__main__": main() # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/data/sessioninstaller.service0000644000000000000000000000012011377143264022076 0ustar 00000000000000[D-BUS Service] Name=org.freedesktop.PackageKit Exec=/usr/bin/session-installer sessioninstaller-0.20+bzr150/doc/example.catalog0000644000000000000000000000205111305154735017737 0ustar 00000000000000[PackageKit Catalog] # Just a package on all versions of fedora that can provide the dependency. # If there are multiple packages then the user will be asked to choose InstallProvides(fedora)=audio/QCELP # Just for Fedora 9, install two development files InstallPackages(fedora;9.90)=glib2;devel;PolicyKit;gnome-devel;ocaml;json;wheel-devel # On any distro, install the package with this file #InstallFiles=/usr/bin/fontinst # For each architecture on Fedora 8, install one of the two different files InstallFiles(fedora;8;i686)=/usr/lib/pango/1.6.0/modules/pango-arabic-fc.so InstallFiles(fedora;8;x64)=/usr/lib64/pango/1.6.0/modules/pango-arabic-fc.so InstallPackages(debian)=xterm InstallPackages(evilos;unstable;i386)=wrong-os Installpackages(debian;sid;i386)=xterm Installpackages(debian;unstable;i386)=xterm Installpackages(debian;sid;am64)=wrong-arch Installpackages(debian;unstable;amd64)=wrong-arch; Installpackages=xterm installpackages(debian;i386)=wrong;ignore;wrong-dist-id installpackages(debian;sid)=xterm installpackages(debian;unstable)=xterm sessioninstaller-0.20+bzr150/doc/gst-install.10000644000000000000000000000231311367222021017264 0ustar 00000000000000.\" groff -man -Tascii foo.1 .TH SESSIONINSTALLER 1 "April 2010" Linux "User manual" .SH NAME gst-install \- installs missing GStreamer plugins .SH SYNOPSIS .B gst-install .RI [ OPTIONS ] plugin .B ... .SH DESCRIPTION .B Gst-install makes use of the PackageKit session D-Bus interface to install missing GStreamer plugins, e.g. encoders, decoders or URI handlers. It isn't supposed to be executed directly but instead by the .BR gst_install_plugins_async " and " gst_install_plugins functions of the GStreamer base library .BR gstpbutilsinstallplugins . PackageKit or the alternative session-installer implementation will handle the complete installation process with confirmation, progress and error dialogs. See the documentation of .B gstpbutilsinstallplugins for the argument construction and exit states. .SH OPTIONS .IP --transient-for The X Window ID of the window which should be used as parents for the dialogs .SH DIAGNOSTICS None yet. .SH HOMEPAGE https://launchpad.net/sessioninstaller .SH BUGS You can report bugs at the Launchpad site of aptdaemon: https://bugs.launchpad.net/sessioninstaller/+filebug .SH AUTHOR Sebastian Heinlein .SH SEE ALSO .BR aptd (1) " session-installer" (1) sessioninstaller-0.20+bzr150/doc/org.freedesktop.PackageKit.xml0000644000000000000000000003640511301022215022566 0ustar 00000000000000 ]> The interface used for quering the package database. Finds out if the package is installed. A package name, e.g. hal-info An optional interaction mode, e.g. timeout=10 If the package is installed. Finds the package name for an installed or available file A package name, e.g. /usr/share/help/gimp/index.html An optional interaction mode, e.g. timeout=10 If the package is installed. The package name of the file, e.g. hal-info The interface used for modifying the package database. Installs local package files or service packs. The X window handle ID, used for focus stealing prevention and setting modality. An array of file names. An interaction mode that specifies which UI elements should be shown or hidden different from the user default, e.g. hide-confirm-search,hide-confirm-deps,hide-confirm-install,show-progress. The show options are: show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning. The hide options are: hide-confirm-search,hide-confirm-deps,hide-confirm-install,hide-progress,hide-finished,hide-warning. Convenience options such as: never, defaults or always. are also available. Installs packages to provide files. The X window handle ID, used for focus stealing prevention and setting modality. An array of file names. An optional interaction mode, e.g. show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning Installs specified package catalogs. The X window handle ID, used for focus stealing prevention and setting modality. An array of catalog filenames. An optional interaction mode, e.g. show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning Installs packages from a configured software source. The X window handle ID, used for focus stealing prevention and setting modality. An array of package names. An optional interaction mode, e.g. show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning Installs mimetype handlers from a configured software source. The X window handle ID, used for focus stealing prevention and setting modality. An array of mime types, e.g. text/plain An optional interaction mode, e.g. show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning Installs fontconfig resources (usually fonts) from a configured software source. The X window handle ID, used for focus stealing prevention and setting modality. An array of font descriptors from fontconfig, e.g. :lang=mn An optional interaction mode, e.g. show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning Installs GStreamer fontconfig resources (usually codecs) from a configured software source. The X window handle ID, used for focus stealing prevention and setting modality. An array of codecs descriptors from pk-gstreamer-install, e.g. Advanced Streaming Format (ASF) demuxer|decoder-video/x-ms-asf An optional interaction mode, e.g. show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning Removes packages that provide the given local files. The X window handle ID, used for focus stealing prevention and setting modality. An array of file names. An interaction mode that specifies which UI elements should be shown or hidden different from the user default, e.g. hide-confirm-search,hide-confirm-deps,hide-confirm-install,show-progress. The show options are: show-confirm-search,show-confirm-deps,show-confirm-install,show-progress,show-finished,show-warning. The hide options are: hide-confirm-search,hide-confirm-deps,hide-confirm-install,hide-progress,hide-finished,hide-warning. Convenience options such as: never, defaults or always. are also available. sessioninstaller-0.20+bzr150/doc/session-installer.10000644000000000000000000000317411652352513020516 0ustar 00000000000000.\" groff -man -Tascii foo.1 .TH SESSIONINSTALLER 1 "December 2009" Linux "User manual" .SH NAME session-installer \- allows applications to easily install and remove software .SH SYNOPSIS .B session-installer .SH DESCRIPTION .B Session-installer allows applications to easily install additional software (e.g. extensions or GSreamer codecs), uninstall files and perform simple package status queries by calling a distribution neutral D-Bus interface. The whole process of confirmation, error reporting and progress notification is handled by sessioninstaller. Currently it comes only with a GTK based user interface. The D-Bus interface is developed under the PackageKit umbrella and is available on the session bus. The reference implementation can be found in gnome-packagekit. It was also adpoted by KPackageKit. In contrast to gnome-packagekit and KPackageKit it doesn't use the PackageKit daemon for querying and installation, but instead makes use of APT and aptdaemon directly (alternatively synaptic can be used). Normally it is not required to start this programme manually since it makes use of D-Bus activation and will be started on request automatically. .SH ENVIRONMENT .IP SESSIONINSTALLER_BACKEND Set the package manager which should be used for installation. Possible values are .IR aptdaemon ", " synaptic " and " dummy . .SH HOMEPAGE https://launchpad.net/sessioninstaller .SH BUGS You can report bugs at the Launchpad site of aptdaemon: https://bugs.launchpad.net/sessioninstaller/+filebug .SH AUTHOR Sebastian Heinlein .SH SEE ALSO .BR aptd(1) .B http://www.packagekit.org/pk-faq.html#session-methods sessioninstaller-0.20+bzr150/po/de.po0000644000000000000000000002174511367114302015556 0ustar 00000000000000# German translation of sessioninstaller # Copyright (C) 2010 Sebastian Heinlein # This file is distributed under the same license as the sessioninstaller # package. # Sebastian Heinlein , 2010 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: sessioninstaller VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-05-01 16:30+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Sebastian Heinlein \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: ../test.py:40 msgid "Install the given packages" msgstr "Angebene Pakete installieren" #: ../test.py:43 msgid "Install package files" msgstr "Packet-Dateien installieren" #: ../test.py:46 msgid "Remove the packages which provide the given files" msgstr "Packete mit den angegebenen Dateien entfernen" #: ../test.py:50 msgid "Check if a package is installed" msgstr "" #: ../test.py:53 msgid "Search for the package providing the given file" msgstr "" #: ../test.py:57 #, python-format msgid "" "Specify the interaction mode by providing a comma spearated list of the " "following values: %s. This is currently not supported." msgstr "" #: ../test.py:65 msgid "Install the packages specfied in the given PackageKit catalog" msgstr "" #: ../test.py:69 msgid "" "Install the given GStreamer resource. The value 'lazy' can be used as an " "alias for WMV9." msgstr "" #: ../test.py:73 msgid "" "Wait for the given seconds until the action is done. Defaults to 5 Minutes." msgstr "" #: ../test.py:82 #, python-format msgid "Removing files: %s" msgstr "" #: ../test.py:88 #, python-format msgid "Checking if %s is installed" msgstr "" #: ../test.py:92 #, python-format msgid "Searching for %s" msgstr "" #: ../test.py:96 #, python-format msgid "Installing from catalog: %s" msgstr "" #: ../test.py:101 #, python-format msgid "Installing files: %s" msgstr "" #: ../test.py:107 #, python-format msgid "Installing packages: %s" msgstr "" #: ../test.py:115 #, python-format msgid "Installing GStreamer-Resource: %s" msgstr "" #: ../sessioninstaller/core.py:199 msgid "I_nstall" msgstr "" #: ../sessioninstaller/core.py:238 msgid "Package" msgstr "" #: ../sessioninstaller/core.py:271 msgid "" "The use of this software may be restricted in some countries. You must " "verify that one of the following is true:\n" "• These restrictions do not apply in your country of legal residence\n" "• You have permission to use this software (for example, a patent license)\n" "• You are using this software for research purposes only" msgstr "" #: ../sessioninstaller/core.py:503 msgid "Failed to install multiple package files" msgstr "" #. FIXME: should provide some information about how to get fonts #: ../sessioninstaller/core.py:505 msgid "" "Installing more than one package file at the same time isn't supported. " "Please install one after the other." msgstr "" #: ../sessioninstaller/core.py:509 msgid "Relative path to package file" msgstr "" #: ../sessioninstaller/core.py:510 msgid "You have to specify the absolute path to the package file." msgstr "" #: ../sessioninstaller/core.py:513 msgid "Unsupported package format" msgstr "" #: ../sessioninstaller/core.py:514 msgid "Only Debian packages are supported (*.deb)" msgstr "" #: ../sessioninstaller/core.py:540 msgid "Installing packages by files isn't supported" msgstr "" #: ../sessioninstaller/core.py:541 msgid "This method hasn't yet been implemented." msgstr "" #: ../sessioninstaller/core.py:578 ../sessioninstaller/core.py:588 #: ../sessioninstaller/core.py:595 msgid "Catalog could not be read" msgstr "" #. TRANSLATORS: %s is a file path #: ../sessioninstaller/core.py:580 #, python-format msgid "The catalog file '%s' doesn't exist." msgstr "" #. TRANSLATORS: %s is a file path #: ../sessioninstaller/core.py:590 #, python-format msgid "The catalog file '%s' could not be opened and and read." msgstr "" #. TRANSLATORS: %s is a file path #: ../sessioninstaller/core.py:597 #, python-format msgid "" "The file '%s' isn't a valid software catalog. Please redownload or contanct " "the provider." msgstr "" #: ../sessioninstaller/core.py:606 msgid "Catalog is not supported" msgstr "" #: ../sessioninstaller/core.py:607 #, python-format msgid "" "The method '%s' which is used to specify packages isn't supported.\n" "Please contact the provider of the catalog about this issue." msgstr "" #: ../sessioninstaller/core.py:627 msgid "A required package is not installable" msgid_plural "Required packages are not installable" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: %s is the name of the missing packages #: ../sessioninstaller/core.py:632 #, python-format msgid "" "The catalog requires the installation of the package %s which is not " "available." msgid_plural "" "The catalog requires the installation of the following packages which are " "not available: %s" msgstr[0] "" msgstr[1] "" #. Create nice messages #: ../sessioninstaller/core.py:645 msgid "Install the following software package?" msgid_plural "Install the following software packages?" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: %s is the name of the application which requested #. the installation #: ../sessioninstaller/core.py:651 #, python-format msgid "%s requires the installation of an additional software package." msgid_plural "%s requires the installation of additional software packages." msgstr[0] "" msgstr[1] "" #. TRANSLATORS: %s is an absolute file path, e.g. /usr/bin/xterm #: ../sessioninstaller/core.py:658 #, python-format msgid "The package catalog %s requests to install the following software." msgid_plural "The following catalogs request to install software: %s" msgstr[0] "" msgstr[1] "" #: ../sessioninstaller/core.py:713 msgid "Searching for file type handler isn't supported" msgstr "" #: ../sessioninstaller/core.py:714 msgid "" "Currently searching and installation of application which can handle special " "files isn't supported." msgstr "" #: ../sessioninstaller/core.py:743 msgid "Installing printer drivers on request isn't supported" msgstr "" #: ../sessioninstaller/core.py:744 msgid "" "Currently autodetection and installation of missing printer drivers is not " "supported." msgstr "" #: ../sessioninstaller/core.py:771 msgid "Installing fonts on request isn't supported" msgstr "" #: ../sessioninstaller/core.py:772 msgid "" "Currently autodetection and installation of missing fonts is not supported." msgstr "" #: ../sessioninstaller/core.py:808 msgid "Invalid search term" msgstr "" #: ../sessioninstaller/core.py:809 #, python-format msgid "The following term doesn't describe a GStreamer resource: %s" msgstr "" #. TRANSLATORS: %s is the application requesting the plugins #: ../sessioninstaller/core.py:839 #, python-format msgid "%s requires extra plugins to handle the following media content type:" msgid_plural "" "%s requires extra plugins to handle following media content types:" msgstr[0] "" msgstr[1] "" #: ../sessioninstaller/core.py:845 msgid "Extra plugins to handle the following media content type are required:" msgid_plural "" "Extra plugins to handle following media content types are required:" msgstr[0] "" msgstr[1] "" #. FIXME: Add more info and possible solutions for the user #: ../sessioninstaller/core.py:909 msgid "Required plugin could not be found" msgstr "" #. Show a confirmation dialog #: ../sessioninstaller/core.py:915 msgid "Install extra multimedia plugin?" msgid_plural "Install extra multimedia plugins?" msgstr[0] "" msgstr[1] "" #: ../sessioninstaller/core.py:958 msgid "Files are not installed" msgstr "" #: ../sessioninstaller/core.py:959 msgid "" "The files which should be removed are not part of any installed software." msgstr "" #: ../sessioninstaller/backends/dummy.py:39 msgid "Removing packages" msgstr "" #: ../sessioninstaller/backends/dummy.py:40 #, python-format msgid "The following packages will be removed with interaction mode %s: %s" msgstr "" #: ../sessioninstaller/backends/dummy.py:45 msgid "Installing packages" msgstr "" #: ../sessioninstaller/backends/dummy.py:46 #, python-format msgid "The following packages will be installed with interaction mode %s: %s" msgstr "" #: ../sessioninstaller/backends/dummy.py:51 msgid "Installing package files" msgstr "" #: ../sessioninstaller/backends/dummy.py:52 #, python-format msgid "" "The following package files will be installed with interaction mode %s: %s" msgstr "" #: ../sessioninstaller/gstinstaller.py:45 msgid "Initializing GStreamerInstaller" msgstr "" #: ../sessioninstaller/gstinstaller.py:66 #, python-format msgid "Ignoring codec: %s" msgstr "" #: ../sessioninstaller/gstinstaller.py:76 #, python-format msgid "Add provide: %s" msgstr "" #: ../sessioninstaller/gstinstaller.py:91 msgid "The X Window ID of the calling application" msgstr "" #: ../sessioninstaller/gstinstaller.py:100 msgid "Cancelled" msgstr "" #: ../sessioninstaller/gstinstaller.py:103 msgid "Could not find any packages to operate on" msgstr "" #: ../sessioninstaller/gstinstaller.py:108 msgid "Finsihed succesfully" msgstr "" sessioninstaller-0.20+bzr150/sessioninstaller/__init__.py0000644000000000000000000000163612561621633021726 0ustar 00000000000000# -*- coding: utf-8 -*- """ sessioninstaller - APT based installer using the PackageKit DBus API Copyright (C) 2008 Sebastian Heinlein 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ __author__ = "Sebastian Heinlein " __state__ = "experimental" # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/backends/0000755000000000000000000000000011352607325021360 5ustar 00000000000000sessioninstaller-0.20+bzr150/sessioninstaller/core.py0000755000000000000000000021171212561627115021121 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- """core - APT based installer using the PackageKit DBus interface""" # Copyright (C) 2008-2009 Sebastian Heinlein # Copyright (C) 2009 Richard Hughes # Copyright (C) 2008 Sebastian Dröge # # 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 # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. __author__ = "Sebastian Heinlein " __state__ = "experimental" from configparser import ConfigParser import functools import locale import logging import os import re import subprocess import time import apt import apt.debfile import apt_pkg from aptdaemon.policykit1 import get_pid_from_dbus_name from defer import defer, inline_callbacks, return_value from defer.utils import dbus_deferred_method import dbus import dbus.service import dbus.mainloop.glib from gettext import gettext as _ import gettext from gi.repository import Gio from gi.repository import GObject from gi.repository import Gst from gi.repository import Gtk from gi.repository import Pango from xdg.DesktopEntry import DesktopEntry from xdg.Exceptions import ParsingError from . import utils from . import errors _backend_env = os.getenv("SESSIONINSTALLER_BACKEND", "aptdaemon") if _backend_env == "synaptic": from .backends.synaptic import SynapticBackend as Backend elif _backend_env == "aptdaemon": from .backends.aptd import AptDaemonBackend as Backend else: from .backends.dummy import DummyBackend as Backend gettext.textdomain("sessioninstaller") gettext.bindtextdomain("sessioninstaller") dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) PACKAGEKIT_QUERY_DBUS_INTERFACE = "org.freedesktop.PackageKit.Query" PACKAGEKIT_MODIFY_DBUS_INTERFACE = "org.freedesktop.PackageKit.Modify" PACKAGEKIT_DBUS_PATH = "/org/freedesktop/PackageKit" PACKAGEKIT_DBUS_SERVICE = "org.freedesktop.PackageKit" INTERACT_NEVER = 0 INTERACT_CONFIRM_SEARCH = 1 INTERACT_CONFIRM_DEPS = 2 INTERACT_CONFIRM_INSTALL = 4 INTERACT_PROGRESS = 8 INTERACT_FINISHED = 16 INTERACT_WARNING = 32 INTERACT_UNKNOWN = 64 INTERACT_ALWAYS = 127 GSTREAMER_RECORD_MAP = {"encoder": "Gstreamer-Encoders", "decoder": "Gstreamer-Decoders", "urisource": "Gstreamer-Uri-Sources", "urisink": "Gstreamer-Uri-Sinks", "element": "Gstreamer-Elements"} RESTRICTED_010_PACKAGES = ["gstreamer0.10-plugins-bad", "gstreamer0.10-plugins-bad-multiverse", "gstreamer0.10-ffmpeg", "gstreamer0.10-plugins-ugly"] RESTRICTED_10_PACKAGES = ["gstreamer1.0-plugins-bad", "gstreamer1.0-libav", "gstreamer1.0-plugins-ugly"] GSTREAMER_010_SCORING = {"gstreamer0.10-plugins-good": 100, "gstreamer0.10-fluendo-mp3": 90, "gstreamer0.10-ffmpeg": 79, "gstreamer0.10-plugins-ugly": 80, "gstreamer0.10-plugins-bad": 70, "gstreamer0.10-plugins-bad-multiverse": 60} GSTREAMER_10_SCORING = {"gstreamer1.0-plugins-good": 100, # "gstreamer1.0-fluendo-mp3": 90, "gstreamer1.0-libav": 79, "gstreamer1.0-plugins-ugly": 80, "gstreamer1.0-plugins-bad": 70} logging.basicConfig(format="%(levelname)s:%(message)s") log = logging.getLogger("sessioninstaller") (COLUMN_NAME, COLUMN_DESC, COLUMN_INSTALL, COLUMN_DETAILS, COLUMN_TOOLTIP, COLUMN_SCORE) = list(range(6)) DAEMON_IDLE_TIMEOUT = 3 * 60 DAEMON_IDLE_CHECK_INTERVAL = 30 # Required to get translated descriptions try: locale.setlocale(locale.LC_ALL, "") except locale.Error: log.debug("Failed to unset LC_ALL") def track_usage(func): """Decorator to keep track of running methods and to update the time stamp of the last action. """ @functools.wraps(func) def _track_usage(*args, **kwargs): def _release_track(ret, si): si._tracks -= 1 si._last_timestamp = time.time() log.debug("Updating last_timestamp") return ret si = args[0] si._tracks += 1 si._last_timestamp = time.time() log.debug("Updating last_timestamp") deferred = defer(func, *args, **kwargs) deferred.add_callbacks(_release_track, _release_track, callback_args=[si], errback_args=[si]) return deferred return _track_usage class GStreamerStructure(object): """Abstraction class of GStramer structure.""" def __init__(self, name="", version="", kind="", record="", caps="", element=""): self.name = name self.version = version self.kind = kind self.record = record self.caps = caps self.element = element self.satisfied = False self.best_provider = None self.best_score = -1 class GtkOpProgress(apt.progress.base.OpProgress): """A simple helper that keeps the GUI alive.""" def __init__(self, progress=None): apt.progress.base.OpProgress.__init__(self) self.progress_dialog = progress def update(self, percent=None): while Gtk.events_pending(): Gtk.main_iteration() if self.progress_dialog: self.progress_dialog.progress.pulse() class ErrorDialog(Gtk.MessageDialog): """Allows to show an error message to the user.""" def __init__(self, title, message, parent=None): GObject.GObject.__init__(self, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.CLOSE) if parent: self.realize() self.set_transient_for(parent) self.set_markup("%s\n\n%s" % (title, message)) class ProgressDialog(Gtk.Dialog): """Allows to show the progress of an ongoing action to the user.""" def __init__(self, title, message, parent=None): Gtk.Dialog.__init__(self) self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) if parent: self.realize() self.set_transient_for(parent) self.set_title(title) self.set_resizable(False) self.set_border_width(12) self.vbox.set_spacing(24) self.label = Gtk.Label() self.label.set_markup("%s\n\n%s" % (title, message)) self.label.set_line_wrap(True) self.vbox.add(self.label) self.progress = Gtk.ProgressBar() self.progress.set_pulse_step(0.01) self.vbox.add(self.progress) self.cancelled = False self.connect("response", self._on_response) def _on_response(self, dialog, response): if response == Gtk.ResponseType.CANCEL: self.cancelled = True class ConfirmInstallDialog(Gtk.Dialog): """Allow to confirm an installation.""" def __init__(self, title, message, pkgs=[], parent=None, details=None, package_type=None, selectable=False, action=None): Gtk.Dialog.__init__(self) self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) if parent: self.realize() self.set_transient_for(parent) self.set_title(title) self.set_resizable(True) self.set_border_width(12) self.vbox.set_spacing(12) self.icon = Gtk.Image.new_from_stock(Gtk.STOCK_DIALOG_QUESTION, Gtk.IconSize.DIALOG) self.icon.set_alignment(0 ,0) hbox_base = Gtk.HBox() hbox_base.set_spacing(24) vbox_left = Gtk.VBox() vbox_left.set_spacing(12) hbox_base.pack_start(self.icon, False, True, 0) hbox_base.pack_start(vbox_left, True, True, 0) self.label = Gtk.Label() self.label.set_alignment(0, 0) self.label.set_markup("%s\n\n%s" % (title, message)) self.label.set_line_wrap(True) vbox_left.pack_start(self.label, False, True, 0) self.cancelled = False self.vbox.pack_start(hbox_base, True, True, 0) if not action: action = _("_Install") self.install_button = self.add_button(action, Gtk.ResponseType.OK) self.set_default_response(Gtk.ResponseType.OK) # Show a list of the plugin packages self.pkg_store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_BOOLEAN, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_INT) self.pkg_store.set_sort_column_id(COLUMN_SCORE, Gtk.SortType.DESCENDING) self.pkg_view = Gtk.TreeView(self.pkg_store) self.pkg_view.set_rules_hint(True) self.pkg_view.props.has_tooltip = True self.pkg_view.connect("query-tooltip", self._on_query_tooltip) if selectable: toggle_install = Gtk.CellRendererToggle() toggle_install.connect("toggled", self._on_toggled_install) column_install = Gtk.TreeViewColumn(_("Install"), toggle_install, active=COLUMN_INSTALL) self.pkg_view.append_column(column_install) if not package_type: package_type = _("Package") column_desc = Gtk.TreeViewColumn(package_type) renderer_warn = Gtk.CellRendererPixbuf() renderer_warn.props.stock_size = Gtk.IconSize.MENU column_desc.pack_start(renderer_warn, False) column_desc.set_cell_data_func(renderer_warn, self._render_warning) renderer_desc = Gtk.CellRendererText() renderer_desc.props.ellipsize = Pango.EllipsizeMode.END column_desc.pack_start(renderer_desc, True) column_desc.add_attribute(renderer_desc, "markup", COLUMN_DESC) column_desc.props.expand = True column_desc.props.resizable = True column_desc.props.min_width = 50 self.pkg_view.append_column(column_desc) if details: renderer_details = Gtk.CellRendererText() renderer_details.props.ellipsize = Pango.EllipsizeMode.END column_details = Gtk.TreeViewColumn(details, renderer_details) column_details.add_attribute(renderer_details, "markup", COLUMN_DETAILS) column_details.props.resizable = True column_details.props.min_width = 50 self.pkg_view.append_column(column_details) if not (selectable or details): self.pkg_view.props.headers_visible = False self.scrolled_window = Gtk.ScrolledWindow() self.scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER) self.scrolled_window.set_shadow_type(Gtk.ShadowType.IN) self.scrolled_window.add(self.pkg_view) vbox_left.pack_start(self.scrolled_window, True, True, 0) self.install_button.props.sensitive = False for pkg in pkgs: self.add_confirm_package(pkg) def add_confirm(self, name, summary, active=True, details="", score=0, restricted=False): """Add an entry to the confirmation dialog. Keyword arguments: name -- name of the package or file summary -- short description of the package active -- if the package should be selected by default details -- additional information about the package score -- the ranking of the package which should be used for ordering restricted -- if the use or redistribution is restriceted """ if restricted: #TRANSLATORS: %s is the name of a piece of software tooltip = _("The use of %s may be restricted in some " "countries. You must verify that one of the following " "is true:\n" "- These restrictions do not apply in your country " "of legal residence\n" "- You have permission to use this software (for " "example, a patent license)\n" "- You are using this software for research " "purposes only") % name # Set the dialog default to cancel if a restricted packages is # selected for installation if active: self.set_default_response(Gtk.ResponseType.CANCEL) else: tooltip = "" desc = utils.get_package_desc(name, summary) self.pkg_store.append((name, desc, active, details, tooltip, score)) if active: self.install_button.props.sensitive = True def add_confirm_package(self, pkg, active=True, details="", score=0): """Show an apt.package.Package instance in the confirmation dialog. Keyword arguments: pkg -- the apt.package.Package instance active -- if the package should be selected by default details -- additional information about the package score -- the ranking of the package which should be used for ordering """ self.add_confirm(pkg.name, pkg.candidate.summary, active, details, score, _is_package_restricted(pkg)) def get_selected_pkgs(self): """Return a list of the package names which are selected.""" return [pkg \ for pkg, _desc, active, _details, _tooltip, _score \ in self.pkg_store \ if active] def _on_query_tooltip(self, treeview, x, y, keyboard_tip, tooltip): """Handle tooltips for restrcited packages.""" try: result, out_x, out_y, model, path, iter = treeview.get_tooltip_context(x, y, keyboard_tip) if not result: return False except TypeError: return False text = model[path][COLUMN_TOOLTIP] if not text: return False tooltip.set_icon_from_icon_name(Gtk.STOCK_DIALOG_WARNING, Gtk.IconSize.DIALOG) tooltip.set_markup(text) treeview.set_tooltip_row(tooltip, path) return True def _on_toggled_install(self, toggle, path): """Handle an activated package.""" cur = toggle.get_active() self.pkg_store[path][COLUMN_INSTALL] = not cur for row in self.pkg_store: if row[COLUMN_INSTALL]: self.install_button.props.sensitive = True return self.install_button.props.sensitive = False def _render_warning(self, cell, renderer, model, iter, data): """Show a warning icon for restricted packages.""" if model.get_value(iter, COLUMN_TOOLTIP): renderer.props.stock_id = Gtk.STOCK_DIALOG_WARNING else: renderer.props.stock_id = None def run(self): """Run the dialog.""" if len(self.pkg_store) > 4: self.scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.pkg_view.set_size_request(-1, 240) self.show_all() res = Gtk.Dialog.run(self) self.hide() self.destroy() return res class SessionInstaller(dbus.service.Object): """Provides the PackageKit session API.""" def __init__(self, bus=None): log.info("Starting service") self.loop = GObject.MainLoop() if not bus: bus = dbus.SessionBus() self.bus = bus bus_name = dbus.service.BusName(PACKAGEKIT_DBUS_SERVICE, bus) dbus.service.Object.__init__(self, bus_name, PACKAGEKIT_DBUS_PATH) self._cache = None self.backend = Backend() GObject.timeout_add_seconds(DAEMON_IDLE_CHECK_INTERVAL, self._check_for_inactivity) self._tracks = 0 self._last_timestamp = time.time() def _check_for_inactivity(self): """Quit after a period of inactivity.""" idle_time = time.time() - self._last_timestamp log.debug("Checking for inactivity (%is)", idle_time) if not self._tracks and \ not GObject.main_context_default().pending() and \ idle_time > DAEMON_IDLE_TIMEOUT: self.loop.quit() log.info("Shutting down because of inactivity") return True def run(self): """Start listening for requests.""" self.loop.run() def _init_cache(self, progress=None): """Helper to set up the package cache.""" if not self._cache: self._cache = apt.Cache(GtkOpProgress(progress)) @inline_callbacks def _get_sender_name(self, sender): """Try to resolve the name of the calling application.""" pid = yield get_pid_from_dbus_name(sender, self.bus) try: exe = utils.get_process_exe(pid) except: return_value(None) # Try to get the name of an interpreted script if re.match("(/usr/bin/python([0-9]\.[0-9])?)|(/usr/bin/perl)", exe): try: exe = utils.get_process_cmdline(pid)[1] except (IndexError, OSError): pass # Special case for the GStreamer codec installation via the helper # gnome-packagekit returns the name of parent window in this case, # But this could be misleading: # return_value(parent.property_get("WM_NAME")[2]) # So we try to identify the calling application if exe in ["/usr/libexec/pk-gstreamer-install", "/usr/lib/pk-gstreamer-install", "/usr/bin/gst-install", "/usr/bin/gstreamer-codec-install"]: try: parent_pid = utils.get_process_ppid(pid) exe = utils.get_process_exe(parent_pid) except OSError: return_value(None) # Return the application name in the case of an installed application for app in Gio.app_info_get_all(): if (app.get_executable() == exe or app.get_executable() == os.path.basename(exe)): return_value(app.get_name()) return_value(os.path.basename(exe)) def _parse_interaction(self, interaction): mode = 0 interact_list = interaction.split(",") if "always" in interact_list: mode = INTERACT_ALWAYS elif "never" in interact_list: mode = INTERACT_NEVER if "show-confirm-progress" in interact_list: mode &= INTERACT_CONFIRM_PROGRESS elif "show-confirm-deps" in interact_list: mode &= INTERACT_CONFIRM_DEPS elif "show-confirm-install" in interact_list: mode &= INTERACT_CONFIRM_INSTALL elif "show-progress" in interact_list: mode &= INTERACT_PROGRESS elif "show-finished" in interact_list: mode &= INTERACT_FINISHED elif "show-warning" in interact_list: mode &= INTERACT_WARNING elif "hide-confirm-progress" in interact_list: mode |= INTERACT_CONFIRM_PROGRESS elif "hide-confirm-deps" in interact_list: mode |= INTERACT_CONFIRM_DEPS elif "hide-confirm-install" in interact_list: mode |= INTERACT_CONFIRM_INSTALL elif "hide-progress" in interact_list: mode |= INTERACT_PROGRESS elif "hide-finished" in interact_list: mode |= INTERACT_FINISHED elif "hide-warning" in interact_list: mode |= INTERACT_WARNING return mode @dbus_deferred_method(PACKAGEKIT_QUERY_DBUS_INTERFACE, in_signature="ss", out_signature="b") def IsInstalled(self, package_name, interaction): """Return True if the given package is installed. Keyword arguments: package-name -- the name of the package, e.g. xterm interaction -- the interaction mode, e.g. timeout=10 """ log.info("IsInstalled() was called: %s, %s", package_name, interaction) return self._is_installed(package_name, interaction) @track_usage def _is_installed(self, package_name, interaction): self._init_cache() try: return self._cache[package_name].is_installed except KeyError: raise errors.QueryNoPackagesFound @dbus_deferred_method(PACKAGEKIT_QUERY_DBUS_INTERFACE, in_signature="ss", out_signature="bs") def SearchFile(self, file_name, interaction): """Return the installation state and name of the package which contains the given file. Keyword arguments: file_name -- the to be searched file name interaction -- the interaction mode, e.g. timeout=10 """ log.info("SearchFile() was called: %s, %s", file_name, interaction) return self._search_file(file_name, interaction) @track_usage def _search_file(self, file_name, interaction): self._init_cache() # Search for installed files if file_name.startswith("/"): pattern = "^%s$" % file_name.replace("/", "\/") else: pattern = ".*\/%s$" % file_name re_file = re.compile(pattern) for pkg in self._cache: # FIXME: Fix python-apt try: for installed_file in pkg.installed_files: if re_file.match(installed_file): #FIXME: What about a file in multiple conflicting # packages? return pkg.is_installed, pkg.name except: pass # Optionally make use of apt-file's Contents cache to search for not # installed files. But still search for installed files additionally # to make sure that we provide up-to-date results if os.path.exists("/usr/bin/apt-file"): #FIXME: Make use of rapt-file on Debian if the network is available #FIXME: Show a warning to the user if the apt-file cache is several # weeks old log.debug("Using apt-file") if file_name.startswith("/"): pattern = "^%s$" % file_name[1:].replace("/", "\/") else: pattern = "\/%s$" % file_name cmd = ["/usr/bin/apt-file", "--regexp", "--non-interactive", "--package-only", "find", pattern] log.debug("Calling: %s" % cmd) apt_file = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = apt_file.communicate() if apt_file.returncode == 0: #FIXME: Actually we should check if the file is part of the # candidate, e.g. if unstable and experimental are # enabled and a file would only be part of the # experimental version #FIXME: Handle multiple packages for pkg_name in stdout.split(): try: pkg = self._cache[pkg_name] except: continue return pkg.isInstalled, pkg.name else: raise errors.QueryInternatlError("apt-file call failed") return False, "" @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="", sender_keyword="sender") def InstallPackageFiles(self, xid, files, interaction, sender): """Install local package files. Keyword arguments: xid -- the window id of the requesting application files -- the list of package file paths interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallPackageFiles was called: %s, %s, %s", xid, files, interaction) return self._install_package_files(xid, files, interaction, sender) @track_usage @inline_callbacks def _install_package_files(self, xid, files, interaction, sender): parent = None # should get from XID, but removed from Gdk 3 header = "" if len(files) != 1: header = _("Failed to install multiple package files") message = _("Installing more than one package file at the same " "time isn't supported. Please install one after the " "other.") elif not files[0][0] == "/": header = _("Relative path to package file") message = _("You have to specify the absolute path to the " "package file.") elif not files[0].endswith(".deb"): header = _("Unsupported package format") message = _("Only Debian packages are supported (*.deb)") try: debfile = apt.debfile.DebPackage(files[0]) desc = debfile["Description"].split("\n", 1)[0] except: header = _("Unsupported package format") message = _("Only Debian packages are supported (*.deb)") if header: dia = ErrorDialog(header, message) dia.run() dia.hide() raise errors.ModifyFailed("%s - %s" % (header, message)) title = gettext.ngettext("Install package file?", "Install package files?", len(files)) sender_name = yield self._get_sender_name(sender) if sender_name: message = gettext.ngettext("%s requests to install the following " "package file.", "%s requests to install the following " "package files.", len(files)) % sender_name message += "\n\n" else: message = "" message += _("Software from foreign sources could be malicious, " "could contain security risks and or even break your " "system." "Install packages from your distribution's " "repositories as far as possible.") confirm = ConfirmInstallDialog(title, message, parent=parent) confirm.add_confirm(files[0], desc) if confirm.run() == Gtk.ResponseType.CANCEL: raise errors.ModifyCancelled yield self.backend.install_package_files(xid, confirm.get_selected_pkgs(), interaction) @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="", sender_keyword="sender") def InstallProvideFiles(self, xid, files, interaction, sender): """Install packages which provide the given files. Keyword arguments: xid -- the window id of the requesting application files -- the list of package file paths interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallProvideFiles() was called: %s, %s, %s", xid, files, interaction) return self._install_provide_files(xid, files, interaction, sender) @track_usage @inline_callbacks def _install_provide_files(self, xid, files, interaction, sender): #FIXME: Reuse apt-file from the search_file method try: from CommandNotFound import CommandNotFound except ImportError: log.warning("command-not-found not supported") else: cnf = CommandNotFound("/usr/share/command-not-found") to_install = list() for executable in [os.path.basename(f) for f in files]: list_of_packages_and_components = cnf.getPackages(executable) if list_of_packages_and_components: (package, component) = list_of_packages_and_components[0] to_install.append(package) if to_install: yield self._install_package_names(xid, to_install, interaction, sender) raise StopIteration # FIXME: show a message here that the binaries were not # found instead of falling through to the misleading # other error message # FIXME: use a different error message header = _("Installing packages by files isn't supported") message = _("This method hasn't yet been implemented.") #FIXME: should provide some information about how to find apps dia = ErrorDialog(header, message) dia.run() dia.hide() raise errors.ModifyInternalError(message) @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="", sender_keyword="sender") def InstallCatalogs(self, xid, files, interaction, sender): """Install packages which provide the given files. Keyword arguments: xid -- the window id of the requesting application files -- the list of catalog file paths interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallCatalogs() was called: %s, %s, %s", xid, files, interaction) return self._install_catalogs(xid, files, interaction, sender) @track_usage @inline_callbacks def _install_catalogs(self, xid, files, interaction, sender): parent = None # should get from XID, but removed from Gdk 3 self._init_cache() arch = os.popen("/usr/bin/dpkg --print-architecture").read().strip() distro, code, release = os.popen("/usr/bin/lsb_release " "--id --code --release " "--short").read().split() regex = "^(?P[a-z]+)(\(%s(;((%s)|(%s))(;%s)?)?\))?$" % \ (distro, code, release, arch) re_action = re.compile(regex, flags=re.IGNORECASE) pkgs = set() missing = set() for catalog_file in files: if not os.path.exists(catalog_file): header = _("Catalog could not be read") #TRANSLATORS: %s is a file path message = _("The catalog file '%s' doesn't " "exist.") % catalog_file self._show_error(header, message) raise errors.ModifyFailed(message) catalog = ConfigParser() try: catalog.read(catalog_file) except: header = _("Catalog could not be read") #TRANSLATORS: %s is a file path message = _("The catalog file '%s' could not be opened " "and read.") % catalog_file self._show_error(header, message) raise errors.ModifyFailed(message) if not catalog.sections() == ["PackageKit Catalog"]: header = _("Catalog could not be read") #TRANSLATORS: %s is a file path message = _("The file '%s' isn't a valid software catalog. " "Please redownload or contact the " "provider.") % catalog_file self._show_error(header, message) raise errors.ModifyFailed(message) for key, value in catalog.items("PackageKit Catalog"): match = re_action.match(key) if match: if match.group("action") != "installpackages": header = _("Catalog is not supported") message = _("The method '%s' which is used to specify " "packages isn't supported.\n" "Please contact the provider of the " "catalog about this " "issue.") % match.group("action") self._show_error(header, message) raise errors.ModifyFailed(message) for pkg_name in value.split(";"): if pkg_name in self._cache: pkg = self._cache[pkg_name] if not pkg.is_installed and not pkg.candidate: missing.add(pkg_name) else: pkgs.add(self._cache[pkg_name]) else: missing.add(pkg_name) else: log.debug("Ignoring catalog instruction: %s" % key) # Error out if packages are not available if missing: header = gettext.ngettext("A required package is not installable", "Required packages are not installable", len(missing)) pkgs = " ".join(missing) #TRANSLATORS: %s is the name of the missing packages msg = gettext.ngettext("The catalog requires the installation of " "the package %s which is not available.", "The catalog requires the installation of " "the following packages which are not " "available: %s", len(missing)) % pkgs self._show_error(header, msg) raise errors.ModifyNoPackagesFound(msg) parent = None # should get from XID, but removed from Gdk 3 # Create nice messages sender_name = yield self._get_sender_name(sender) title = gettext.ngettext("Install the following software package?", "Install the following software packages?", len(pkgs)) if sender_name: #TRANSLATORS: %s is the name of the application which requested # the installation message = gettext.ngettext("%s requires the installation of an " "additional software package.", "%s requires the installation of " "additional software packages.", len(pkgs)) % sender_name else: #TRANSLATORS: %s is an absolute file path, e.g. /usr/bin/xterm message = gettext.ngettext("The package catalog %s requests to " "install the following software.", #TRANSLATORS: %s is a list of absoulte file paths "The following catalogs request to " "install software: %s", #TRANSLATORS: %s is an absolute file path, e.g. /usr/bin/xterm len(files)) % " ".join("'%s'") confirm = ConfirmInstallDialog(title, message, pkgs, parent) res = confirm.run() if res == Gtk.ResponseType.OK: yield self.backend.install_packages(xid, confirm.get_selected_pkgs(), interaction) else: raise errors.ModifyCancelled @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="", sender_keyword="sender") def InstallPackageNames(self, xid, packages, interaction, sender): """Install packages from a preconfigured software source. Keyword arguments: xid -- the window id of the requesting application packages -- the list of package names interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallPackageNames() was called: %s, %s, %s", xid, packages, interaction) return self._install_package_names(xid, packages, interaction, sender) @track_usage @inline_callbacks def _install_package_names(self, xid, packages, interaction, sender): parent = None # should get from XID, but removed from Gdk 3 title = gettext.ngettext("Install additional software package?", "Install additional software packages?", len(packages)) sender_name = yield self._get_sender_name(sender) if sender_name: message = gettext.ngettext("%s requests to install the following " "software package to provide additional " "features.", "%s requests to install the following " "software packages to provide " "additional features.", len(packages)) % sender_name else: message = gettext.ngettext("The following software package is " "required to provide additional " "features.", "The following software packages are " "required to provide additional " "features.", len(packages)) self._init_cache() confirm = ConfirmInstallDialog(title, message, parent=parent) failed_packages = [] for pkg_name in packages: try: pkg = self._cache[pkg_name] except KeyError: failed_packages.append(pkg_name) continue if not pkg.candidate: failed_packages.append(pkg_name) continue if not pkg.is_installed: confirm.add_confirm_package(pkg) if failed_packages: header = gettext.ngettext("Could not find requested package", "Could not find requested packages", len(failed_packages)) if sender_name: message = gettext.ngettext("%s requests to install the " "following software package to " "provide " "additional features:", "%s requests to install the " "following " "software packages to provide " "additional features:", len(packages)) % sender_name else: message = gettext.ngettext("The following software package " "is required to provide " "additional features but cannot " "be installed:", "The following software " "packages are required to " "provide " "additional features but cannot " "be installed:", len(failed_packages)) message += self._get_bullet_list(failed_packages) self._show_error(header, message) raise errors.ModifyNoPackagesFound(header) # If all packages are already installed we return silently if len(confirm.pkg_store) > 0: if confirm.run() == Gtk.ResponseType.CANCEL: raise errors.ModifyCancelled yield self.backend.install_packages(xid, confirm.get_selected_pkgs(), interaction) @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="", sender_keyword="sender") def InstallMimeTypes(self, xid, mime_types, interaction, sender): """Install mime type handler from a preconfigured software source. Keyword arguments: xid -- the window id of the requesting application mime_types -- list of mime types whose handlers should be installed interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallMimeTypes() was called: %s, %s, %s", xid, mime_types, interaction) return self._install_mime_types(xid, mime_types, interaction, sender) @track_usage @inline_callbacks def _install_mime_types(self, xid, mime_types_list, interaction, sender): parent = None # should get from XID, but removed from Gdk 3 if not os.path.exists(utils.APP_INSTALL_DATA): #FIXME: should provide some information about how to find apps header = _("Installing mime type handlers isn't supported") message = _("To search and install software which can open " "certain file types you have to install " "app-install-data.") dia = ErrorDialog(header, message) dia.run() dia.hide() raise errors.ModifyInternalError(message) sender_name = yield self._get_sender_name(sender) title = _("Searching for suitable software to open files") mime_types = set(mime_types_list) if sender_name: #TRANSLATORS: %s is an application message = gettext.ngettext("%s requires to install software to " "open files of the following file type:", "%s requires to install software to " "open files of the following file " "types:", len(mime_types)) % sender_name else: message = gettext.ngettext("Software to open files of the " "following file type is required " "but is not installed:", "Software to open files of the " "following file types is required " "but is not installed:", len(mime_types)) mime_types_desc = [Gio.content_type_get_description(mime_type) \ for mime_type in mime_types] message += self._get_bullet_list(mime_types_desc) progress = ProgressDialog(title, message, parent) progress.show_all() while Gtk.events_pending(): Gtk.main_iteration() # Search the app-install-data desktop files for mime type handlers self._init_cache(progress) package_map = {} unsatisfied = mime_types.copy() mixed = False for count, path in enumerate(os.listdir(utils.APP_INSTALL_DATA)): if path[0] == "." or not path.endswith(".desktop"): continue if not count % 20: while Gtk.events_pending(): Gtk.main_iteration() progress.progress.pulse() if progress.cancelled: progress.hide() progress.destroy() raise errors.ModifyCancelled try: desktop_entry = DesktopEntry(os.path.join(utils.APP_INSTALL_DATA, path)) except ParsingError: continue pkg_name = desktop_entry.get("X-AppInstall-Package") try: if self._cache[pkg_name].is_installed: continue except KeyError: continue supported_mime_types = set(desktop_entry.getMimeTypes()) for mime_type in supported_mime_types: if not mime_type in mime_types: continue package_map.setdefault(pkg_name, [[], set(), 0]) #FIXME: Don't add desktop entries twice package_map[pkg_name][0].append(desktop_entry) desc = Gio.content_type_get_description(mime_type) package_map[pkg_name][1].add(desc) popcon = desktop_entry.get("X-AppInstall-Popcon", type="integer") if package_map[pkg_name][2] < popcon: package_map[pkg_name][2] = popcon unsatisfied.discard(mime_type) if not mixed and \ not supported_mime_types.issuperset(mime_types): mixed = True progress.hide() progress.destroy() if mixed or unsatisfied: details = _("Supported file types") else: details = None title = _("Install software to open files?") if unsatisfied: unsatisfied_desc = [Gio.content_type_get_description(mime_type) \ for mime_type in unsatisfied] unsatisfied_str = self._get_bullet_list(unsatisfied_desc) message += "\n\n" #TRANSLATORS: %s is either a single file type or a bullet list of # file types message += gettext.ngettext("%s is not supported.", "Unsupported file types: %s", len(unsatisfied)) % unsatisfied_str confirm = ConfirmInstallDialog(title, message, parent=parent, details=details, selectable=len(package_map) > 1, package_type=_("Application")) for pkg, (entries, provides, score) in package_map.items(): if len(provides) == len(mime_types): details = _("All") else: #TRANSLATORS: Separator for a list of plugins details = _(",\n").join(provides) confirm.add_confirm_package(self._cache[pkg], len(package_map) == 1, details, score) res = confirm.run() if res == Gtk.ResponseType.OK: yield self.backend.install_packages(xid, confirm.get_selected_pkgs(), interaction) else: raise errors.ModifyCancelled @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="") def InstallPrinterDrivers(self, xid, resources, interaction): """Install printer drivers from from a preconfigured software source. Keyword arguments: xid -- the window id of the requesting application resources -- a list of printer model descriptors in IEEE 1284 Device ID format e.g. "MFG:Hewlett-Packard" "MDL:HP LaserJet6MP" interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallPrinterDrivers() was called: %s, %s, %s", xid, resources, interaction) return self._install_printer_drivers(xid, resources, interaction) @track_usage def _install_printer_drivers(self, xid, resources, interaction): header = _("Installing printer drivers on request isn't supported") message = _("Currently autodetection and installation of " "missing printer drivers is not supported.") #FIXME: should provide some information about how to get printers dia = ErrorDialog(header, message) dia.run() dia.hide() raise errors.ModifyInternalError(message) @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="") def InstallFontconfigResources(self, xid, resources, interaction,): """Install fontconfig resources from from a preconfigured software source. Keyword arguments: xid -- the window id of the requesting application resources -- list of fontconfig resources (usually fonts) interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallFontconfigResources() was called: %s, %s, %s", xid, resources, interaction) return self._install_fontconfig_resources(xid, resources, interaction) @track_usage def _install_fontconfig_resources(self, xid, resources, interaction): header = _("Installing fonts on request isn't supported") message = _("Currently autodetection and installation of " "missing fonts is not supported.") #FIXME: should provide some information about how to get fonts dia = ErrorDialog(header, message) dia.run() dia.hide() raise errors.ModifyInternalError(message) @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="", sender_keyword="sender") def InstallGStreamerResources(self, xid, resources, interaction, sender): """Install GStreamer resources from from a preconfigured software source. Keyword arguments: xid -- the window id of the requesting application resources -- list of GStreamer structures, e.g. "gstreamer0.10(decoder-video/x-wmv)(wmvversion=3)" interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("InstallGstreamerResources() was called: %s, %s, %s", xid, resources, interaction) Gst.init(None) return self._install_gstreamer_resources(xid, resources, interaction, sender) @track_usage @inline_callbacks def _install_gstreamer_resources(self, xid, resources, interaction, sender): def parse_gstreamer_structure(resource): # E.g. "MS Video|gstreamer0.10(decoder-video/x-wmv)(wmvversion=3)" match = re.match("^(?P.*)\|gstreamer(?P[0-9\.]+)" "\((?P.+?)-(?P.+?)\)" "(?P\(.+\))?$", resource) caps = None element = None if not match: title = _("Invalid search term") message = _("The following term doesn't describe a " "GStreamer resource: %s") % resource self._show_error(title, message) raise errors.ModifyFailed(message) if match.group("kind") in ["encoder", "decoder"]: caps_str = "%s" % match.group("structname") if match.group("fields"): for field in re.findall("\((.+?=(\(.+?\))?.+?)\)", match.group("fields")): caps_str += ", %s" % field[0] # gst.Caps.__init__ cannot handle unicode instances caps = Gst.Caps.from_string(str(caps_str)) else: element = match.group("structname") record = GSTREAMER_RECORD_MAP[match.group("kind")] return GStreamerStructure(match.group("name"), match.group("version"), match.group("kind"), record, caps, element) structures = [parse_gstreamer_structure(res) for res in resources] kinds = set([struct.kind for struct in structures]) # Show a progress dialog parent = None # should get from XID, but removed from Gdk 3 sender_name = yield self._get_sender_name(sender) title = _("Searching for multimedia plugins") # Get a nice dialog message if kinds.issubset(set(["encoder"])): if sender_name: #TRANSLATORS: %s is the application requesting the plugins message = gettext.ngettext("%s requires to install plugins to " "create media files of the " "following type:", "%s requires to install plugins to " "create files of the following " "types:", len(structures)) % sender_name else: message = gettext.ngettext("The plugin to create media files " "of the following type is not " "installed:", "The plugin to create media files " "of the following types is not " "installed:", len(structures)) elif kinds.issubset(set(["decoder"])): if sender_name: #TRANSLATORS: %s is the application requesting the plugins message = gettext.ngettext("%s requires to install plugins to " "play media files of the " "following type:", "%s requires to install plugins to " "play files of the following " "types:", len(structures)) % sender_name else: message = gettext.ngettext("The plugin to play media files " "of the following type is not " "installed:", "The plugin to play media files " "of the following types is not " "installed:", len(structures)) elif kinds.issubset(set(["encoder", "decoder"])): if sender_name: #TRANSLATORS: %s is the application requesting the plugins message = gettext.ngettext("%s requires to install plugins to " "create and play media files of the " "following type:", "%s requires to install plugins to " "create and play media files of the " "following types:", len(structures)) % sender_name else: message = gettext.ngettext("The plugins to create and play " "media files of the following type " "are not installed:", "The plugins to create and play " "media files of the following types " "are not installed:", len(structures)) else: if sender_name: #TRANSLATORS: %s is the application requesting the plugins message = gettext.ngettext("%s requires to install plugins to " "support the following " "multimedia feature:", "%s requires to install plugins to " "support the following multimedia " "features:", len(structures)) % sender_name else: message = gettext.ngettext("Extra plugins to provide the " "following multimedia feature are " "not installed:", "Extra plugins to provide the " "following multimedia features are " "not installed:", len(structures)) message += self._get_bullet_list([struct.name or struct.record \ for struct in structures]) progress = ProgressDialog(title, message, parent) progress.show_all() while Gtk.events_pending(): Gtk.main_iteration() # Search the package cache for packages providing the plugins pkgs = [] partial_providers = False self._init_cache(progress) # Get the architectures with an installed gstreamer library # Unfortunately the architecture isn't part of the request. So we # have to detect for which architectuers gstreamer has been installed # on the system, to avoid showing codecs for not used but enabeled # architecures, see LP #899001 architectures = apt_pkg.get_architectures() supported_archs = set() if len(architectures) > 1: for gst_version in set([struct.version for struct in structures]): for arch in architectures: try: pkg = self._cache["libgstreamer%s-0:%s" % (gst_version, arch)] except KeyError: continue if pkg.is_installed: supported_archs.add(arch) else: supported_archs = architectures for count, pkg in enumerate(self._cache): if not count % 100: while Gtk.events_pending(): Gtk.main_iteration() progress.progress.pulse() if progress.cancelled: progress.hide() progress.destroy() raise errors.ModifyCancelled if (pkg.is_installed or not pkg.candidate or not "Gstreamer-Version" in pkg.candidate.record or not pkg.candidate.architecture in supported_archs): continue # Check if the package could not be free in usage or distribution # Allow to prefer special packages try: pkg_name = pkg.name.split(":")[0] if pkg_name in GSTREAMER_010_SCORING: score = GSTREAMER_010_SCORING[pkg_name] elif pkg_name in GSTREAMER_10_SCORING: score = GSTREAMER_10_SCORING[pkg_name] else: raise KeyError except KeyError: score = 0 if _is_package_restricted(pkg): score -= 10 provides = [] for struct in structures: if pkg.candidate.record["Gstreamer-Version"].split(".")[0] != struct.version.split(".")[0]: continue if struct.caps: try: pkg_caps = Gst.Caps.from_string(pkg.candidate.record[struct.record]) except KeyError: continue if pkg_caps.intersect(struct.caps).is_empty(): continue else: try: elements = pkg.candidate.record[struct.record] except KeyError: continue if not struct.element in elements: continue provides.append(struct.name) struct.satisfied = True if score > struct.best_score: struct.best_provider = pkg.name.split(":")[0] struct.best_score = score if provides: provides_all = len(structures) == len(provides) if not provides_all: partial_providers = True pkgs.append((pkg, provides_all, provides, score)) progress.hide() progress.destroy() # Error out if there isn't any package available if not pkgs: #FIXME: Add more info and possible solutions for the user dia = ErrorDialog(_("Required plugin could not be found"), message) dia.run() dia.hide() raise errors.ModifyNoPackagesFound # Show a confirmation dialog title = gettext.ngettext("Install extra multimedia plugin?", "Install extra multimedia plugins?", len(pkgs)) unsatisfied = [stru.name for stru in structures if not stru.satisfied] if unsatisfied: message += "\n\n" message += gettext.ngettext("The following plugin is not " "available:", "The following plugins are not " "available:", len(unsatisfied)) message += " " #TRANSLATORS: list separator message += self._get_bullet_list(unsatisfied) # We only show the details if there are packages which would only # provide a subset of the requests if partial_providers: details = _("Provides") else: details = None best_providers = set([struct.best_provider for struct in structures]) confirm = ConfirmInstallDialog(title, message, parent=parent, details=details, selectable=len(pkgs) > 1, package_type=_("Plugin Package")) for pkg, provides_all, provides, score in pkgs: if provides_all: details = _("All") else: #TRANSLATORS: Separator for a list of plugins details = _(",\n").join(provides) # Skip the architecture from the name install = pkg.name.split(":")[0] in best_providers confirm.add_confirm_package(pkg, install, details, score) res = confirm.run() if res == Gtk.ResponseType.OK: yield self.backend.install_packages(xid, confirm.get_selected_pkgs(), interaction) else: raise errors.ModifyCancelled @dbus_deferred_method(PACKAGEKIT_MODIFY_DBUS_INTERFACE, in_signature="uass", out_signature="", sender_keyword="sender") def RemovePackageByFiles(self, xid, files, interaction, sender): """Remove packages which provide the given files. Keyword arguments: xid -- the window id of the requesting application files -- the list of file paths interaction -- the interaction mode: which ui elements should be shown e.g. hide-finished or hide-confirm-search """ log.info("RemovePackageByFiles() was called: %s, %s, %s", xid, files, interaction) return self._remove_package_by_files(xid, files, interaction, sender) @track_usage @inline_callbacks def _remove_package_by_files(self, xid, files, interaction, sender): parent = None # should get from XID, but removed from Gdk 3 sender_name = yield self._get_sender_name(sender) if [filename for filename in files if not filename.startswith("/")]: raise errors.ModifyFailed("Only absolute file names") pkgs = [] title = _("Searching software to be removed") if sender_name: message = gettext.ngettext("%s wants to remove the software " "which provides the following file:", "%s wants to remove the software which " "provides the following files:", len(files)) % sender_name else: message = gettext.ngettext("The software which provides the " "following file is requested to be " "removed:" "The software which provides the " "following files is requested to be " "removed:", len(files)) message += self._get_bullet_list(files) progress = ProgressDialog(title, message, parent) progress.show_all() self._init_cache(progress) for pkg in self._cache: try: for installed_file in pkg.installed_files: if installed_file in files: pkgs.append(pkg) except: pass progress.hide() if not pkgs: self._show_error(_("Files are not installed"), _("The files which should be removed are not " "part of any installed software.")) raise errors.ModifyNoPackagesFound title = gettext.ngettext("Remove software package?", "Remove software packages?", len(pkgs)) if sender_name: #TRANSLATORS: %s is the name of an application message = gettext.ngettext("%s wants to remove the following " "software package from your computer.", "%s wants to remove the following " "software packages from your computer.", len(pkgs)) % sender_name else: message = gettext.ngettext("The following software package " "will be removed from your computer.", "The following software packages " "will be removed from your computer.", len(pkgs)) confirm = ConfirmInstallDialog(title, message, parent=parent, selectable=len(pkgs) > 1, pkgs=pkgs, action=_("_Remove")) res = confirm.run() if res == Gtk.ResponseType.OK: yield self.backend.remove_packages(xid, confirm.get_selected_pkgs(), interaction) else: raise errors.ModifyCancelled def _show_error(self, header, message): """Show an error dialog.""" dialog = ErrorDialog(header, message) dialog.run() dialog.hide() def _get_bullet_list(self, lst): """Return a string with a bullet list for the given list.""" text = "" if len(lst) == 1: text += " " text += lst[0] else: for element in lst: text += "\n• %s" % element return text def _is_package_restricted(pkg): """If a package is possibly restricted in use.""" return (pkg.name.split(":")[0] in RESTRICTED_010_PACKAGES + RESTRICTED_10_PACKAGES \ or pkg.candidate.origins[0].component in ("non-free", "restricted", "multiverse")) def main(): log.setLevel(logging.DEBUG) si = SessionInstaller() si.run() if __name__ == "__main__": main() # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/errors.py0000644000000000000000000000613712561621633021504 0ustar 00000000000000# -*- coding: utf-8 -*- """Exception classes""" # Copyright (C) 2010 Sebastian Heinlein # # 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 # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. __author__ = "Sebastian Heinlein " import dbus from functools import wraps import inspect import sys import types __all__ = ["convert_dbus_exception", "get_native_exception"] # Create the exception classes on the fly def _declare_module(): module = types.ModuleType("_errors") for err in ["Failed", "Cancelled", "NoPackagesFound", "InternalError", "Forbidden"]: for interface in ["Query", "Modify"]: name = "%s%s" % (interface, err) cls = type(name, (dbus.DBusException,), {"_dbus_error_name": \ "org.freedesktop.Packagekit.%s.%s" % (interface, err)}) setattr(module, name, cls) __all__.append(name) return module _errors = _declare_module() sys.modules["_errors"] = _errors from _errors import * def convert_dbus_exception(func): """A decorator which maps a raised DBbus exception to a native one.""" argnames, varargs, kwargs, defaults = inspect.getargspec(func) @wraps(func) def _convert_dbus_exception(*args, **kwargs): try: error_handler = kwargs["error_handler"] except KeyError: _args = list(args) try: index = argnames.index("error_handler") error_handler = _args[index] except ValueError: pass else: _args[index] = \ lambda err: error_handler(get_native_exception(err)) args = tuple(_args) else: kwargs["error_handler"] = \ lambda err: error_handler(get_native_exception(err)) try: return func(*args, **kwargs) except dbus.exceptions.DBusException as error: raise get_native_exception(error) return _convert_dbus_exception def get_native_exception(error): """Map a DBus exception to a native one. This allows to make use of try/except on the client side without having to check for the error name. """ dbus_name = error.get_dbus_name() dbus_msg = error.get_dbus_message() for attr in _errors.__dict__.values(): try: if dbus_name == attr._dbus_error_name: return attr(dbus_msg) except AttributeError: continue return error # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/gstinstaller.py0000755000000000000000000001056212561623432022702 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- """gstinstaller - install GStreamer components via PackageKit session service""" # Copyright (C) 2010 Sebastian Heinlein # # 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 # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. __author__ = "Sebastian Heinlein " from gettext import gettext as _ import logging import optparse import re import sys import dbus from .core import PACKAGEKIT_MODIFY_DBUS_INTERFACE, \ PACKAGEKIT_DBUS_PATH, \ PACKAGEKIT_DBUS_SERVICE from . import errors logging.basicConfig(format="%(levelname)s:%(message)s") log = logging.getLogger("GStreamerInstaller") class GStreamerInstaller(object): """Provides an installer of GStreamer components""" def __init__(self, xid, codecs): log.info("Initializing GStreamerInstaller") self.xid = dbus.UInt32(xid) self.provides = self._get_provides(codecs) bus = dbus.SessionBus() self.pk = bus.get_object(PACKAGEKIT_DBUS_SERVICE, PACKAGEKIT_DBUS_PATH, False) def _get_provides(self, codecs): provides = dbus.Array(signature="s") # E.g. "gstreamer|0.10|totem|Windows Media Video 9 decoder| # decoder-video/x-wmv, wmvversion=(int)3, fourcc=(fourcc)WVC1, # format=(fourcc)WVC1" # See http://gstreamer.freedesktop.org/data/doc/gstreamer/head/ # gst-plugins-base-libs/html/ # gst-plugins-base-libs-gstpbutilsinstallplugins.html regex = re.compile("^gstreamer\|(?P[0-9])+\.(?P[0-9]+)\|" "(?P.+)\|(?P.+)\|(?P[a-z]+?)-" "(?P.+?)(,(?P.+))?[|]?$") for codec in codecs: match = regex.match(codec) if not match: log.warn("Ignoring codec: %s", codec) continue provide = "%s|gstreamer%s.%s(%s-%s)" % (match.group("desc"), match.group("major"), match.group("minor"), match.group("type"), match.group("name")) if match.group("fields"): for field in match.group("fields").split(","): provide += "(%s)" % field.strip() log.debug("Add provide: %s", provide) provides.append(provide) return provides @errors.convert_dbus_exception def run(self): self.pk.InstallGStreamerResources(self.xid, self.provides, "hide-finished", timeout=360000, dbus_interface=PACKAGEKIT_MODIFY_DBUS_INTERFACE) def main(): parser = optparse.OptionParser() parser.add_option("", "--transient-for", action="store", type="int", dest="xid", default="0", help=_("The X Window ID of the calling application")) options, args = parser.parse_args() installer = GStreamerInstaller(options.xid, args) # See the documentation of gstpbutilsinstallplugins for the exit state # definitions. Unluckily the PackageKit session interface doesn't support # partial codec installation try: installer.run() except errors.ModifyCancelled: log.warn("Cancelled") sys.exit(4) except errors.ModifyNoPackagesFound: log.critical("Could not find any packages to operate on") sys.exit(1) except Exception as error: log.exception(error) sys.exit(2) log.info("Finished succesfully") sys.exit(0) if __name__ == "__main__": log.setLevel(logging.DEBUG) main() # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/utils.py0000644000000000000000000000771312561627115021332 0ustar 00000000000000# -*- coding: utf-8 -*- """util -- Apt-Xapian-Index integration""" # Copyright (c) 2010 Sebastian Heinlein # # 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 # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. __author__ = "Sebastian Heinlein " __state__ = "experimental" import logging import os from xdg.DesktopEntry import DesktopEntry __ALL__ = ("get_package_desc", "APP_INSTALL_DATA", "AXI_DATABASE") APP_INSTALL_DATA = "/usr/share/app-install/desktop" AXI_DATABASE = "/var/lib/apt-xapian-index/index" log = logging.getLogger("sessioninstaller") try: import xapian os.stat(APP_INSTALL_DATA) try: axi = xapian.Database("/var/lib/apt-xapian-index/index") except xapian.DatabaseOpeningError: axi = None except (ImportError, OSError): axi = None if not axi: log.warning("Falling back to package information") _desktop_cache = {} def _load(file_name): path = os.path.join(APP_INSTALL_DATA, file_name) return DesktopEntry(path) def get_package_desc(pkg, summary): """Return a pango markup description of the package. If the package provides one or more applications use the name and comment of the applications. """ markup = "" try: pkg, arch = pkg.split(":") except ValueError: arch = None if axi: for m in axi.postlist("XP" + pkg): doc = axi.get_document(m.docid) for term_iter in doc.termlist(): if term_iter.term.startswith("XDF"): if markup: markup += "\n\n" file_name = term_iter.term[3:] de = _desktop_cache.setdefault(file_name, _load(file_name)) app_name = de.getName() app_comment = de.getComment() if app_name: markup += "%s" % app_name if arch: markup += " (%s)" % arch markup += "" if app_comment: markup += "\n%s" % app_comment if not markup: if arch: markup = "%s (%s)" % (pkg, arch) else: markup = "%s" % pkg if summary: markup += "\n%s" % summary return markup def get_process_cmdline(pid): """Return the command line arguments of the given process as a list. :param pid: a process id """ with open("/proc/%s/cmdline" % pid) as fd_cmd: return [arg for arg in fd_cmd.read().split("\x00") if arg] def get_process_ppid(pid): """Return the process id of the parent process. :param pid: a process id """ with open("/proc/%s/status" % pid) as fd_status: for line in fd_status: if line.startswith("PPid:"): return int(line.split()[1]) return None def get_process_exe(pid): """Return the path to the executable of the given process or None if if cannot be found. :param pid: a process id """ # Taken from python-psutil link_exe = "/proc/%s/exe" % pid try: exe = os.readlink(link_exe) except (OSError, IOError): return None # Remove null bytes exe = exe.replace("\x00", "") # Handle deleted files if exe.endswith(" (deleted)") and not os.path.exists(exe): exe = exe[:-10] return exe # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/backends/__init__.py0000644000000000000000000000000011352607325023457 0ustar 00000000000000sessioninstaller-0.20+bzr150/sessioninstaller/backends/aptd.py0000644000000000000000000001314712561621633022671 0ustar 00000000000000# -*- coding: utf-8 -*- """ Provides an aptdaemon based backend """ # Copyright (C) 2008-2010 Sebastian Heinlein # # Licensed under the GNU General Public License Version 2 # # 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. __author__ = "Sebastian Heinlein " from gi.repository import Gtk from aptdaemon import enums import defer import aptdaemon.client import aptdaemon.errors import aptdaemon.gtk3widgets import sessioninstaller.errors class AptDaemonBackend(object): """Provides a graphical test application.""" def __init__(self): self.ac = aptdaemon.client.AptClient() def _run_trans(self, trans, parent, interaction): deferred = defer.Deferred() dia = aptdaemon.gtk3widgets.AptProgressDialog(trans) if parent: dia.realize() dia.set_transient_for(parent) dia.run(close_on_finished=True, show_error=False, reply_handler=lambda: True, error_handler=deferred.errback) dia.connect("finished", self._on_finished, deferred, trans) return deferred def _on_finished(self, diag, deferred, trans): if deferred.called: return # Already called, likely from error_handler (LP: #1056545) if trans.error: deferred.errback(trans.error) else: deferred.callback() def _simulate_trans(self, trans, parent, interaction): deferred = defer.Deferred() trans.simulate(reply_handler=lambda: deferred.callback(trans), error_handler=deferred.errback) return deferred def _confirm_deps(self, trans, parent, interaction): if not [pkgs for pkgs in trans.dependencies if pkgs]: return trans dia = aptdaemon.gtk3widgets.AptConfirmDialog(trans) if parent: dia.realize() dia.set_transient_for(parent) res = dia.run() dia.hide() if res == Gtk.ResponseType.OK: return trans raise sessioninstaller.errors.ModifyCancelled def install_packages(self, xid, package_names, interaction): deferred = defer.Deferred() parent = None # should get from XID, but removed from Gdk 3 self.ac.install_packages(package_names, reply_handler=deferred.callback, error_handler=deferred.errback) deferred.add_callback(self._simulate_trans, parent, interaction) deferred.add_callback(self._confirm_deps, parent, interaction) deferred.add_callback(self._run_trans, parent, interaction) deferred.add_errback(self._show_error, parent) return deferred def install_package_files(self, xid, files, interaction): deferred = defer.Deferred() parent = None # should get from XID, but removed from Gdk 3 #FIXME: Add support for installing serveral files at the same time self.ac.install_file(files[0], reply_handler=deferred.callback, error_handler=deferred.errback) deferred.add_callback(self._simulate_trans, parent, interaction) deferred.add_callback(self._confirm_deps, parent, interaction) deferred.add_callback(self._run_trans, parent, interaction) deferred.add_errback(self._show_error, parent) return deferred def remove_packages(self, xid, package_names, interaction): deferred = defer.Deferred() parent = None # should get from XID, but removed from Gdk 3 self.ac.remove_packages(package_names, reply_handler=deferred.callback, error_handler=deferred.errback) deferred.add_callback(self._simulate_trans, parent, interaction) deferred.add_callback(self._confirm_deps, parent, interaction) deferred.add_callback(self._run_trans, parent, interaction) deferred.add_errback(self._show_error, parent) return deferred def _show_error(self, error, parent): try: error.raise_exception() except aptdaemon.errors.NotAuthorizedError: raise sessioninstaller.errors.ModifyForbidden except aptdaemon.errors.TransactionCancelled: raise sessioninstaller.errors.ModifyCancelled except aptdaemon.errors.TransactionFailed as error: pass except sessioninstaller.errors.ModifyCancelled as error: raise error except Exception as error: error = aptdaemon.errors.TransactionFailed(enums.ERROR_UNKNOWN, str(error)) dia = aptdaemon.gtk3widgets.AptErrorDialog(error) if parent: dia.realize() dia.set_transient_for(parent) dia.run() dia.hide() msg = "%s - %s\n%s" % (enums.get_error_string_from_enum(error.code), enums.get_error_description_from_enum(error.code), error.details) raise sessioninstaller.errors.ModifyFailed(msg) # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/backends/dummy.py0000644000000000000000000000435112561621633023071 0ustar 00000000000000# -*- coding: utf-8 -*- """Provides a dummy backend.""" # Copyright (C) 2008-2010 Sebastian Heinlein # # Licensed under the GNU General Public License Version 2 # # 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. __author__ = "Sebastian Heinlein " from gettext import gettext as _ from gi.repository import Gtk class DummyBackend(object): """Provide some dummy dialogs which simulate package operations.""" def _show_message(self, title, text): dia = Gtk.MessageDialog(buttons=Gtk.ButtonsType.CLOSE, message_format=title) dia.format_secondary_text(text) dia.run() dia.hide() def remove_packages(self, xid, package_names, interaction): title = _("Removing packages") text = _("The following packages will be removed with interaction " "mode %s: %s") % ( interaction, " ".join(package_names)) self._show_message(title, text) def install_packages(self, xid, package_names, interaction): title = _("Installing packages") text = _("The following packages will be installed with interaction " "mode %s: %s") % (interaction, " ".join(package_names)) self._show_message(title, text) def install_package_files(self, xid, package_names, interaction): title = _("Installing package files") text = _("The following package files will be installed with " "interaction mode %s: %s") % (interaction, " ".join(package_names)) self._show_message(title, text) # vim:ts=4:sw=4:et sessioninstaller-0.20+bzr150/sessioninstaller/backends/synaptic.py0000644000000000000000000000676612561627115023605 0ustar 00000000000000# -*- coding: utf-8 -*- """Make use of synaptic as backend.""" # Copyright (C) 2008-2010 Sebastian Heinlein # Copyright (C) 2005-2007 Canonical # # Licensed under the GNU General Public License Version 2 # # 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. __author__ = "Sebastian Heinlein , " \ "Michael Vogt