├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── differential-shellcheck.yml ├── .gitignore ├── .pylintrc ├── ABOUT-NLS ├── AUTHORS ├── COPYING ├── ChangeLog-OLD ├── HIG.py ├── Makefile.am ├── NEWS ├── OpenPrintingRequest.py ├── PhysicalDevice.py ├── README.md ├── SearchCriterion.py ├── ToolbarSearchEntry.py ├── aclocal.m4 ├── applet.py ├── asyncconn.py ├── asyncipp.py ├── asyncpk1.py ├── authconn.py ├── bootstrap ├── check-device-ids.py ├── config.guess ├── config.py.in ├── config.rpath ├── config.sub ├── configure.ac ├── cupshelpers ├── __init__.py ├── config.py.in ├── cupshelpers.py ├── installdriver.py ├── openprinting.py ├── ppds.py └── xmldriverprefs.py ├── cupspk.py ├── data ├── screenshot-mainwindow.png └── screenshot-properties.png ├── dbus ├── com.redhat.NewPrinterNotification.conf ├── com.redhat.PrinterDriversInstaller.conf ├── org.fedoraproject.Config.Printing.service.in ├── org.fedoraproject.Config.Printing.xml └── scp-dbus-service.in ├── debug.py ├── dnssdresolve.py ├── errordialogs.py ├── firewallsettings.py ├── gitlog-to-changelog ├── gtkinklevel.py ├── gui.py ├── icons └── i-network-printer.png ├── install-printerdriver.in ├── install-printerdriver.py ├── install-sh ├── installpackage.py ├── jobviewer.py ├── killtimer.py ├── man └── system-config-printer.xml ├── mkinstalldirs ├── monitor.py ├── newprinter.py ├── options.py ├── optionwidgets.py ├── po ├── ChangeLog ├── LINGUAS ├── Makefile.in.in ├── Makevars ├── POTFILES.in ├── Rules-quot ├── ar.po ├── as.po ├── ast.po ├── bg.po ├── bn.po ├── bn_IN.po ├── boldquot.sed ├── br.po ├── bs.po ├── ca.po ├── cs.po ├── cy.po ├── da.po ├── de.po ├── el.po ├── en@boldquot.header ├── en@quot.header ├── en_GB.po ├── es.po ├── et.po ├── fa.po ├── fi.po ├── fr.po ├── fur.po ├── gu.po ├── he.po ├── hi.po ├── hr.po ├── hu.po ├── id.po ├── insert-header.sin ├── is.po ├── it.po ├── ja.po ├── ka.po ├── kn.po ├── ko.po ├── lt.po ├── lv.po ├── mai.po ├── ml.po ├── mr.po ├── ms.po ├── nb.po ├── nds.po ├── nl.po ├── nn.po ├── oc.po ├── or.po ├── pa.po ├── pl.po ├── pt.po ├── pt_BR.po ├── quot.sed ├── remove-potcdate.sin ├── ro.po ├── ru.po ├── si.po ├── sk.po ├── sl.po ├── sr.po ├── sr@latin.po ├── sv.po ├── system-config-printer.pot ├── ta.po ├── te.po ├── th.po ├── tr.po ├── uk.po ├── vi.po ├── zh_CN.po ├── zh_Hans.po └── zh_TW.po ├── ppdcache.py ├── ppdippstr.py ├── ppdsloader.py ├── print-applet.desktop.in ├── printerproperties.py ├── probe_printer.py ├── profile-ppds.py ├── pyproject.toml ├── pysmb.py ├── scp-dbus-service.py ├── serversettings.py ├── setup.py ├── smburi.py ├── statereason.py ├── system-config-printer-applet.in ├── system-config-printer.appdata.xml.in ├── system-config-printer.desktop.in ├── system-config-printer.in ├── system-config-printer.py ├── test └── test-cups-driver.py ├── test_PhysicalDevice.py ├── test_ppds.py ├── timedops.py ├── troubleshoot ├── CheckLocalServerPublishing.py ├── CheckNetworkServerSanity.py ├── CheckPPDSanity.py ├── CheckPrinterSanity.py ├── CheckSELinux.py ├── CheckUSBPermissions.py ├── ChooseNetworkPrinter.py ├── ChoosePrinter.py ├── DeviceListed.py ├── ErrorLogCheckpoint.py ├── ErrorLogFetch.py ├── ErrorLogParse.py ├── LocalOrRemote.py ├── Locale.py ├── NetworkCUPSPrinterShared.py ├── PrintTestPage.py ├── PrinterStateReasons.py ├── QueueNotEnabled.py ├── QueueRejectingJobs.py ├── RemoteAddress.py ├── SchedulerNotRunning.py ├── ServerFirewalled.py ├── Shrug.py ├── VerifyPackages.py ├── Welcome.py ├── __init__.py └── base.py ├── udev ├── .gitignore ├── 70-printers.rules ├── configure-printer@.service.in ├── udev-add-printer └── udev-configure-printer.c ├── ui ├── AboutDialog.ui ├── ConnectDialog.ui ├── ConnectingDialog.ui ├── InstallDialog.ui ├── JobsWindow.ui ├── NewPrinterName.ui ├── NewPrinterWindow.ui ├── PrinterPropertiesDialog.ui ├── PrintersWindow.ui ├── SMBBrowseDialog.ui ├── ServerSettingsDialog.ui ├── WaitWindow.ui ├── remove-gtkalignment.xsl └── statusicon_popupmenu.ui ├── userdefault.py └── xml ├── preferreddrivers.rng ├── preferreddrivers.xml └── validate.py /.gitattributes: -------------------------------------------------------------------------------- 1 | ChangeLog merge=merge-changelog 2 | po/*.po merge=binary 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | version: 2 4 | 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | schedule: 9 | interval: monthly 10 | labels: 11 | - 'type: dependencies' 12 | - 'github-actions' 13 | -------------------------------------------------------------------------------- /.github/workflows/differential-shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: Differential ShellCheck 2 | on: 3 | pull_request: 4 | branches: [master] 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | security-events: write 15 | pull-requests: write 16 | 17 | steps: 18 | - name: Repository checkout 19 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Differential ShellCheck 24 | uses: redhat-plumbers-in-action/differential-shellcheck@dd551ce780d8af741f8cd8bab6982667b906b457 25 | with: 26 | token: ${{ secrets.GITHUB_TOKEN }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.in 2 | Makefile 3 | /configure 4 | /config.status 5 | /config.log 6 | /autom4te.cache 7 | /missing 8 | /compile 9 | /depcomp 10 | /INSTALL 11 | /ChangeLog 12 | TAGS 13 | .deps 14 | .dirstamp 15 | 16 | intltool-extract* 17 | intltool-merge* 18 | intltool-update* 19 | po/.intltool-merge-cache 20 | 21 | .stamp-distutils-in-builddir 22 | *.pyc 23 | *.o 24 | *.gladep 25 | 26 | /test-ppd-module.sh 27 | /pickled-ppds 28 | 29 | po/*.mo 30 | po/*.gmo 31 | po/POTFILES 32 | po/Makefile.in 33 | po/Makefile 34 | po/stamp-it 35 | 36 | # These are generated from *.in files 37 | config.py 38 | my-default-printer 39 | system-config-printer 40 | system-config-printer-applet 41 | manage-print-jobs.desktop 42 | my-default-printer.desktop 43 | print-applet.desktop 44 | system-config-printer.desktop 45 | 46 | # These are compiled 47 | udev/udev-configure-printer 48 | 49 | # These are generated from the XML file. 50 | man/*.1 51 | 52 | # Backup files 53 | *.bak 54 | *~ 55 | 56 | *.sw[nop] 57 | 58 | # Tarballs 59 | system-config-printer-*.tar.?z* 60 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [VARIABLES] 2 | additional-builtins=_ 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Florian Festi 2 | Tim Waugh 3 | -------------------------------------------------------------------------------- /HIG.py: -------------------------------------------------------------------------------- 1 | PAD_SMALL = 3 2 | PAD_NORMAL = 6 3 | PAD_BIG = 12 4 | PAD_LARGE = 18 5 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 1.5.19 changes 2 | -------------- 3 | - Fix `debugprint()` call in options.py (Issue #291) 4 | - Fix exit value if the device is already handled 5 | - Fix building with Python 3.12 (Issue #357, #355) 6 | - Many translation updates 7 | - Fix TypeError in jobviewer.py (Issue #324) 8 | 9 | 1.5.18 changes 10 | -------------- 11 | - Add into the .appdata.xml file (#269) 12 | - Accessiblity improvements (#244) 13 | - system-config-printer couldn't be uninstalled vi GNOME Software (#273) 14 | - system-config-printer crashes due missing libhandy (#283) 15 | - Updated config.sub and config.guess to fix configuration error on RiSC (#282) 16 | - Use pkg-config or --with-cups-serverbin-dir for finding SERVERBIN (#234) 17 | 18 | 1.5.17 changes 19 | -------------- 20 | - Migrate from deprecated splittype to urlparse (#268) 21 | - Support GNOME 42 dark style preference (#263) 22 | 23 | 24 | 1.5.16 changes 25 | -------------- 26 | - fix preserve_job_files default settings 27 | - add debugprint covering failed fingerprint retrieval attempts 28 | - Remove travis 29 | - .travis.yml: run on focal and its newer python 30 | - Make sure that applet.py is running one instance per user 31 | - fix incorrect use of urllib.request 32 | - remove python3-requests 33 | - build: Migrate build system from Intltool to Gettext 34 | - Makefile.am: Remove zanata usage 35 | - udev-configure-printer.c: Fix possible use after frees and leaks 36 | - scp-dbus-service.py: Fix typo in method call 37 | - add option to disable xmlto manual generation 38 | - allow + in device uris - gutenprint has a backend with + (fixes #208) 39 | - updates in README.md: build/install instructions, changes related to s-c-p with CUPS 3.x (IPP services/Printer Applications, no PPDs/drivers/static queues), TODOs, need of new developer(s) 40 | 41 | 1.5.15 changes 42 | -------------- 43 | - set the minimum gettext version because autoconf 2.70 requires it (#201) 44 | - create README file because autoconf requires it (#201) 45 | - full fix for #179 46 | 47 | 1.5.14 changes 48 | -------------- 49 | - set preferred driver for DYMO LabelWriter 400 50 | - udev: ignore devices capable of IPP over USB, we have ipp-usb for it 51 | - asyncpk1.py: dont require the exact Gdk version, work arounds #179 52 | 53 | 1.5.13 changes 54 | -------------- 55 | - add checks for NULL in udev-configure-printer (Fedora #1761097) 56 | - github #174 - put back notification about missing pysmbc 57 | - update .pot file because of fix #174 58 | - python3.9 - xml module removed elem.getchildren() method, use list(elem) 59 | - Make the matching rule of printer device path more flexible (#183) 60 | 61 | 1.5.12 changes 62 | -------------- 63 | - when you use different component id, add provide for old name (issues/99) 64 | - fix other issues in system-config-printer.appdata.xml to be completely valid 65 | for new format 66 | - make the appstream file validate with version >= 0.6 (issues/97) 67 | - fixes for scp-dbus-services (pull/96) 68 | - use ValueError instead of ImportError (pull/95) 69 | - fix constructing the auth dialog (pull/93) 70 | - update da.po (pull/102) 71 | - use utf-8 in fdopen() (pull/112) 72 | - Fallback to using LC_CTYPE if LC_MESSAGES is empty and fix _language use (pull/108) 73 | - Update de.po (pull/106) 74 | - Fix TypeError raised by debugprint call (pull/121) 75 | - dbus: remove deprecated at_console statement (pull/123) 76 | - fixed several memory leaks reported by Coverity scan 77 | - temporary fix for error pop up message for IPP2.0+ attributes (issues/122) 78 | - lpd queue names printed on the console (issues/132) 79 | - use proper docstring (pull/130) 80 | - remove deprecated SIGNAL_RUN_LAST (pull/134) 81 | - use remote ppd for CUPS shared queues (pull/137) 82 | - get rid of warnings in applet (Fedora issue #1732890) 83 | - check if we have required parameters in install-printerdriver (Fedora issue #1754204) 84 | 85 | 1.5.10 changes 86 | -------------- 87 | 88 | - printer couldn't be add ( https://bugzilla.redhat.com/show_bug.cgi?id=1419175 ) 89 | - changes from Ubuntu by Till Kamppeter (pull/64) 90 | - .travis.yml: switch from precise to trusty (pull/63) 91 | - Replace icons deprecated by GTK 3.0 by non-deprecated ones (pull/62) 92 | - Add a StartService for systemd based systems (pull/56) 93 | - French translation update (pull/57) 94 | - Spelling fixes (pull/58) 95 | - Syntax fixes (pull/59) 96 | - Python 3.6 invalid escape sequence deprecation fixes (pull/60) 97 | - Adds printer properties dialog vertical expansion (pull/61) 98 | - Replace icons deprecated by GTK 3.0 by non-deprecated ones (pull/62) 99 | - Improvements of discovered devices/conection type lists in new-printer wizard, more debug output (pull/65) 100 | - replace libgnome-keyring by libsecret (issues/51) 101 | - Do not start the applet on GNOME and Cinnamon desktops (pull/41) 102 | - Do not notify on 'cups-waiting-for-job-completed' because it's not an… (pull/71) 103 | - Updated Turkish translation (pull/74) 104 | - Update tr.po (pull/73) 105 | - TypeError on .update call (issues/76) 106 | - build: Install appstream metadata to non-deprecated location #77 (pull/77) 107 | - Addition of some strings for i18n (pull/81) 108 | - Update .po and .pot files 109 | - added GenericName and X-GNOME-FullName to system-config-printer.desktop.in (issues/20) 110 | - removed some deprecated parts of gui 111 | - updated translations by files from Zanata 112 | - s-c-p doesn't react on ALREADY_ENABLED exception from firewalld 113 | - removed deprecated Gtk objects 114 | - another deprecated issues - GLib 115 | - parent attribute in Gtk.Dialog is deprecated - use transient_for 116 | - system-config-printer.py doesn't have program name (issues/53) 117 | - removed macedonian localization because of low rate of translated strings 118 | - don't ship pre-configured scripts 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # system-config-printer 2 | 3 | It uses IPP to configure a CUPS server. Additionally it provides dBUS interface 4 | for several operations which aren't directly available in cupsd, and automatic USB 5 | printer installation daemon for non-IPP-over-USB printers. 6 | 7 | The alternatives for the graphical configuration tool are CUPS Web 8 | interface, desktop control-center or lpadmin command line tool if you need 9 | to install printer manually which may not be needed in recent cases. 10 | 11 | When I do not need printer setup tool as S-C-P? 12 | ----------------------------------------------- 13 | 14 | If the application where you print from has an up-to-date print dialog 15 | (uses the current CUPS API) and your printer is set up for driverless printing 16 | (has AirPrint/IPP Everywhere/IPP-over-USB support, opened port 631, lies in your local network 17 | and Avahi is running, IPP and mDNS is enabled in your firewall, ipp-usb is installed if 18 | your printer is connected by USB), you don't to install the printer at all. The dialog 19 | is able to pick up the printer right before you open the dialog and it disappears 20 | once you don't need it (reappearing next time you open the dialog again). 21 | This appearing and disappearing printer is called CUPS temporary queue. 22 | 23 | The devices supporting AirPrint/IPP Everywhere appear since 2010 (IPP-over-USB devices a bit later), 24 | so if your device was made after 2010, there is a great chance it supports the standards. 25 | 26 | From the dialogs I'm aware of the temporary queues are currently supported in any GTK3 27 | based application (evince, gvim, gedit, firefox if you are on GNOME and choose system 28 | dialog for printing...) and in Libreoffice. 29 | 30 | Future with CUPS 3.0: 31 | --------------------- 32 | 33 | CUPS 3.0 will divide the current CUPS in several modules - command line tools, library, 34 | CUPS Local server and CUPS Sharing server. CUPS Local server will be designed as lightweight 35 | for desktop usage, supporting only CUPS temporary queues. CUPS Sharing server will be more like 36 | the current cupsd, supporting permanent driverless queues, suited for servers. 37 | 38 | system-config-printer is often used on desktops where will be CUPS Local server installed by default. 39 | Since the server will support only CUPS temporary queues, system-config-printer will need to work 40 | with IPP services which are on localhost (USB devices, printer applications and permanent driverless 41 | queues from CUPS Sharing server if installed as well), in local network or defined by printer profiles, 42 | if system-config-printer should work with CUPS 3.0. 43 | 44 | Is S-C-P required in system with CUPS 3.0? 45 | ------------------------------------------ 46 | 47 | Regarding printer setup tools OpenPrinting current plans are to integrate printer setup dialog into 48 | common print dialog, which would enable non-driverless printer installation (via printer application) 49 | right when user needs it - right before the printing. 50 | 51 | dBUS interface (but probably not the same methods) will be available in CUPS 3.0 project 52 | and since non-driverless devices are less common with time OpenPrinting group agreed there 53 | won't be an automatic installation mechanism for them. 54 | 55 | So the standalone configuration tool like system-config-printer isn't a priority in system with CUPS 3.0, 56 | but it can exist together with CUPS 3.0 if updated. The next point is connected to the matter. 57 | 58 | NEW DEVELOPER OR CO-MAINTAINER WANTED: 59 | -------------------------------------- 60 | 61 | I'm looking for a new developer or co-maintainer for system-config-printer and pycups, 62 | who would update/help to update them to the current standards. 63 | 64 | In case of system-config-printer it consists: 65 | - migration to GTK 4, 66 | - migration of dbus code because dbus-python is deprecated, 67 | - support for installing printer applications from various sources (.rpm, .deb, snap, flatpak), 68 | - support for managing IPP services instead of permanent CUPS queues. 69 | - implementation of unit tests and overall updating the python code to current PEPs 70 | 71 | The project is kept in maintenance mode (accepting and testing sent patches, limited new development) 72 | until there is such a person. 73 | 74 | Build requirements: 75 | ------------------- 76 | 77 | - development packages for: 78 | - cups 79 | - gettext 80 | - glib2 81 | - libusb 82 | - python3 83 | - systemd 84 | - tools: 85 | - autoconf 86 | - autoconf-archives 87 | - automake 88 | - desktop-file-install 89 | - intltool 90 | - xmlto 91 | - gcc 92 | - python3-setuptools 93 | - python3-build 94 | 95 | Runtime requirements: 96 | --------------------- 97 | 98 | - any desktop notification daemon 99 | - dbus-x11 100 | - gobject-introspection 101 | - gtk3 102 | - libnotify 103 | - python3-cairo 104 | - python3-cups 105 | - python3-dbus 106 | - python3-firewall 107 | - python3-gobject 108 | - (optional) python3-smbc 109 | 110 | How to compile and install: 111 | --------------------------- 112 | 113 | ``` 114 | $ ./bootstrap 115 | $ ./configure 116 | $ make 117 | $ sudo make install 118 | ``` 119 | 120 | How to uninstall: 121 | ----------------- 122 | 123 | ``` 124 | $ sudo make uninstall 125 | ``` 126 | 127 | Translations: 128 | ------------- 129 | 130 | Translations are available at [Fedora Weblate](https://translate.fedoraproject.org). 131 | If you want to update translations, please update it in Weblate. The Weblate then creates 132 | automatic PR, which keeps upstream project and Weblate project in synch. 133 | -------------------------------------------------------------------------------- /SearchCriterion.py: -------------------------------------------------------------------------------- 1 | ## Copyright (C) 2008 Rui Matos 2 | 3 | ## This program is free software; you can redistribute it and/or modify 4 | ## it under the terms of the GNU General Public License as published by 5 | ## the Free Software Foundation; either version 2 of the License, or 6 | ## (at your option) any later version. 7 | 8 | ## This program is distributed in the hope that it will be useful, 9 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ## GNU General Public License for more details. 12 | 13 | ## You should have received a copy of the GNU General Public License 14 | ## along with this program; if not, write to the Free Software 15 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | class SearchCriterion: 18 | SUBJECT_NAME = 0 19 | SUBJECT_DESC = 1 20 | SUBJECT_MANUF = 2 21 | SUBJECT_MODEL = 3 22 | SUBJECT_URI = 4 23 | SUBJECT_MEDIA = 5 24 | SUBJECT_STAT = 6 25 | SUBJECT_COUNT = 7 26 | SUBJECT_LOCATION = 8 27 | 28 | RULE_IS = 0 29 | RULE_ISNOT = 1 30 | RULE_CONT = 2 31 | RULE_NOTCONT = 3 32 | RULE_COUNT = 4 33 | 34 | def __init__ (self, 35 | subject = SUBJECT_NAME, 36 | rule = RULE_CONT, 37 | value = ""): 38 | self.subject = subject 39 | self.rule = rule 40 | self.value = value 41 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autopoint --force 3 | aclocal 4 | automake --foreign --copy --add-missing 5 | autoconf 6 | 7 | # If this is a git repository, and git-merge-changelog is available, 8 | # use it. 9 | if [ -d .git ] && git --version 2>/dev/null >/dev/null && \ 10 | git-merge-changelog 2>/dev/null >/dev/null; then 11 | git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' 12 | git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' 13 | fi 14 | -------------------------------------------------------------------------------- /config.py.in: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2006, 2007, 2008, 2014 Red Hat, Inc. 4 | ## Copyright (C) 2006, 2007 Florian Festi 5 | ## Copyright (C) 2006, 2007, 2008 Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | prefix="@prefix@" 22 | datadir="@datadir@" 23 | localedir="@localedir@" 24 | pkgdatadir="@datadir@/@PACKAGE@" 25 | VERSION="@VERSION@" 26 | PACKAGE="@PACKAGE@" 27 | DOWNLOADABLE_ONLYPPD=True 28 | DOWNLOADABLE_ONLYFREE=True 29 | DOWNLOADABLE_PKG_ONLYSIGNED=True 30 | packagesystem = None # discovered at runtime 31 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(system-config-printer, 1.5.18) 2 | AC_CONFIG_SRCDIR(system-config-printer.py) 3 | AM_INIT_AUTOMAKE([dist-xz dist-bzip2 subdir-objects 1.6]) 4 | AM_GNU_GETTEXT_VERSION(0.20) 5 | AX_REQUIRE_DEFINED([AM_GNU_GETTEXT]) 6 | AM_GNU_GETTEXT([external]) 7 | AM_PATH_PYTHON([3]) 8 | PACKAGE="AC_PACKAGE_NAME" 9 | VERSION="AC_PACKAGE_VERSION" 10 | GETTEXT_PACKAGE="AC_PACKAGE_NAME" 11 | CATOBJEXT=".gmo" 12 | DATADIRNAME=share 13 | AC_SUBST(PACKAGE) 14 | AC_SUBST(VERSION) 15 | AC_SUBST(GETTEXT_PACKAGE) 16 | AC_SUBST(CATOBJEXT) 17 | AC_SUBST(DATADIRNAME) 18 | 19 | # Let distributor specify if they want to use a vendor with desktop-file-install 20 | AC_ARG_WITH(desktop-vendor, 21 | [AC_HELP_STRING([--with-desktop-vendor], 22 | [Specify the vendor for use in calls to desktop-file-install @<:@default=@:>@])],, 23 | [with_desktop_vendor=""]) 24 | 25 | VENDOR=$with_desktop_vendor 26 | if test "x$VENDOR" = "x"; then 27 | DESKTOPVENDOR= 28 | DESKTOPPREFIX= 29 | else 30 | DESKTOPVENDOR="--vendor $VENDOR" 31 | DESKTOPPREFIX="$VENDOR-" 32 | fi 33 | AC_SUBST(DESKTOPVENDOR) 34 | AC_SUBST(DESKTOPPREFIX) 35 | 36 | PKG_PROG_PKG_CONFIG 37 | 38 | dnl Allow manual definition for CUPS SERVERBIN, if not defined, use value from pkg-config... 39 | AC_MSG_CHECKING([for CUPS SERVERBIN dir]) 40 | AC_ARG_WITH([cups_serverbin_dir], 41 | AS_HELP_STRING([--with-cups-serverbin-dir], [set CUPS SERVERBIN dir, default=value from pkg-config]), [ 42 | cupsserverbindir="$withval" 43 | AC_MSG_RESULT([$withval]) 44 | ], [ 45 | AS_IF([test "x$PKG_CONFIG" != x], [ 46 | AS_IF([$PKG_CONFIG --exists cups], [ 47 | cupsserverbindir=$($PKG_CONFIG --variable=cups_serverbin cups) 48 | AC_MSG_RESULT([$cupsserverbindir]) 49 | ], [ 50 | AC_MSG_ERROR([No CUPS pkgconfig file present and no CUPS SERVERBIN dir defined in configure options.]) 51 | ]) 52 | ], [ 53 | AC_MSG_ERROR([No pkg-config present and no CUPS SERVERBIN dir defined in configure options.]) 54 | ]) 55 | ]) 56 | 57 | AS_IF([test "x$cupsserverbindir" = x], [ 58 | AC_MSG_ERROR([No CUPS SERVERBIN dir defined in CUPS pkgconfig file or configure options.]) 59 | ]) 60 | 61 | AC_SUBST(cupsserverbindir) 62 | 63 | PKG_CHECK_MODULES(GLIB, glib-2.0, has_glib=yes, has_glib=no) 64 | 65 | AC_ARG_WITH(udev-rules, 66 | [AC_HELP_STRING([--with-udev-rules], 67 | [Enable automatic USB print queue configuration @<:@default=no@:>@])], 68 | [], 69 | [with_udev_rules=no]) 70 | AM_CONDITIONAL([UDEV_RULES], [test x$with_udev_rules != xno]) 71 | 72 | AC_ARG_WITH([udevdir], 73 | AS_HELP_STRING([--with-udevdir=DIR], [Directory for udev helper programs]), 74 | [], [with_udevdir=$($PKG_CONFIG --variable=udevdir udev)]) 75 | if test "x$with_udevdir" != xno; then 76 | AC_SUBST([udevdir], [$with_udevdir]) 77 | AC_SUBST([udevrulesdir], [$with_udevdir/rules.d]) 78 | fi 79 | 80 | if test "x$with_udev_rules" != xno -a "x$with_udevdir" != xno; then 81 | PKG_CHECK_MODULES(libudev, [libudev >= 172], has_libudev=yes, has_libudev=no) 82 | PKG_CHECK_MODULES(libusb, libusb-1.0, has_libusb=yes, has_libusb=no) 83 | if test x$has_glib == xno -o \ 84 | x$has_udev == xno -o \ 85 | x$has_libudev == xno -o \ 86 | x$has_libusb == xno ; then 87 | AC_MSG_ERROR([Missing packages]) 88 | fi 89 | 90 | AM_PROG_CC_C_O 91 | fi 92 | 93 | AC_ARG_WITH([systemdsystemunitdir], 94 | AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), 95 | [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) 96 | if test "x$with_systemdsystemunitdir" != xno; then 97 | AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) 98 | fi 99 | AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) 100 | 101 | AC_ARG_WITH(xmlto, 102 | [AC_HELP_STRING([--with-xmlto], 103 | [Enable xmlto manual generation @<:@default=yes@:>@])]) 104 | AM_CONDITIONAL([XMLTO], [test x$with_xmlto != xno]) 105 | 106 | AC_CONFIG_FILES([ 107 | Makefile 108 | po/Makefile.in 109 | system-config-printer 110 | system-config-printer-applet 111 | install-printerdriver 112 | dbus/scp-dbus-service 113 | udev/configure-printer@.service 114 | ]) 115 | AC_OUTPUT 116 | -------------------------------------------------------------------------------- /cupshelpers/__init__.py: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2008, 2011 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | __all__ = ['set_debugprint_fn', 22 | 'Device', 'Printer', 'activateNewPrinter', 23 | 'copyPPDOptions', 'getDevices', 'getPrinters', 24 | 'missingPackagesAndExecutables', 'missingExecutables', 25 | 'parseDeviceID', 26 | 'setPPDPageSize', 27 | 'ppds', 28 | 'openprinting'] 29 | 30 | def _no_debug (x): 31 | return 32 | 33 | _debugprint_fn = _no_debug 34 | def _debugprint (x): 35 | _debugprint_fn (x) 36 | 37 | def set_debugprint_fn (debugprint): 38 | """ 39 | Set debugging hook. 40 | 41 | @param debugprint: function to print debug output 42 | @type debugprint: fn (str) -> None 43 | """ 44 | global _debugprint_fn 45 | _debugprint_fn = debugprint 46 | 47 | from .cupshelpers import \ 48 | Device, \ 49 | Printer, \ 50 | activateNewPrinter, \ 51 | copyPPDOptions, \ 52 | getDevices, \ 53 | getPrinters, \ 54 | missingPackagesAndExecutables, \ 55 | missingExecutables, \ 56 | parseDeviceID, \ 57 | setPPDPageSize 58 | 59 | from . import ppds 60 | from . import openprinting 61 | -------------------------------------------------------------------------------- /cupshelpers/config.py.in: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2006, 2007, 2010 Red Hat, Inc. 4 | ## Authors: 5 | ## Florian Festi 6 | ## Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | prefix="@prefix@" 23 | sysconfdir="@sysconfdir@" 24 | cupsserverbindir="@cupsserverbindir@" 25 | -------------------------------------------------------------------------------- /cupshelpers/installdriver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2010 Red Hat, Inc. 6 | ## Author: Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import dbus 23 | import dbus.glib 24 | import dbus.service 25 | from . import _debugprint, set_debugprint_fn 26 | 27 | class PrinterDriversInstaller(dbus.service.Object): 28 | DBUS_PATH = "/com/redhat/PrinterDriversInstaller" 29 | DBUS_IFACE = "com.redhat.PrinterDriversInstaller" 30 | DBUS_OBJ = "com.redhat.PrinterDriversInstaller" 31 | 32 | def __init__ (self, bus): 33 | self.bus = bus 34 | bus_name = dbus.service.BusName (self.DBUS_OBJ, bus=bus) 35 | dbus.service.Object.__init__ (self, bus_name, self.DBUS_PATH) 36 | 37 | @dbus.service.method(DBUS_IFACE, 38 | in_signature="sss", 39 | async_callbacks=("reply_handler", 40 | "error_handler")) 41 | def InstallDrivers (self, mfg, mdl, cmd, 42 | reply_handler, error_handler): 43 | bus = dbus.SessionBus () 44 | obj = bus.get_object ("org.freedesktop.PackageKit", 45 | "/org/freedesktop/PackageKit") 46 | proxy = dbus.Interface (obj, "org.freedesktop.PackageKit.Modify") 47 | xid = 0 48 | resources = ["MFG:%s;MDL:%s;" % (mfg, mdl)] 49 | interaction = "hide-finished" 50 | _debugprint ("Calling InstallPrinterDrivers (%s, %s, %s)" % 51 | (repr (xid), repr (resources), repr (interaction))) 52 | proxy.InstallPrinterDrivers (dbus.UInt32 (xid), 53 | resources, interaction, 54 | reply_handler=reply_handler, 55 | error_handler=error_handler, 56 | timeout=3600) 57 | 58 | def client_test(): 59 | bus = dbus.SystemBus () 60 | import sys 61 | obj = bus.get_object (PrinterDriversInstaller.DBUS_OBJ, 62 | PrinterDriversInstaller.DBUS_PATH) 63 | proxy = dbus.Interface (obj, PrinterDriversInstaller.DBUS_IFACE) 64 | print (proxy.InstallDrivers ("MFG", "MDL", "CMD")) 65 | -------------------------------------------------------------------------------- /data/screenshot-mainwindow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenPrinting/system-config-printer/1f7c8095c1b3c64a9fbe348a53df4bc74625f3ee/data/screenshot-mainwindow.png -------------------------------------------------------------------------------- /data/screenshot-properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenPrinting/system-config-printer/1f7c8095c1b3c64a9fbe348a53df4bc74625f3ee/data/screenshot-properties.png -------------------------------------------------------------------------------- /dbus/com.redhat.NewPrinterNotification.conf: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /dbus/com.redhat.PrinterDriversInstaller.conf: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /dbus/org.fedoraproject.Config.Printing.service.in: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=org.fedoraproject.Config.Printing 3 | Exec=@bindir@/scp-dbus-service 4 | -------------------------------------------------------------------------------- /dbus/scp-dbus-service.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/scp-dbus-service.py "$@" 4 | -------------------------------------------------------------------------------- /debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2008, 2010 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import sys 22 | import traceback 23 | 24 | _debug=False 25 | def debugprint (x): 26 | if _debug: 27 | try: 28 | sys.stderr.write (x + "\n") 29 | sys.stderr.flush () 30 | except: 31 | pass 32 | 33 | def get_debugging (): 34 | return _debug 35 | 36 | def set_debugging (d): 37 | global _debug 38 | _debug = d 39 | 40 | def fatalException (exitcode=1): 41 | nonfatalException (type="fatal", end="Exiting") 42 | sys.exit (exitcode) 43 | 44 | def nonfatalException (type="non-fatal", end="Continuing anyway.."): 45 | d = get_debugging () 46 | set_debugging (True) 47 | debugprint ("Caught %s exception. Traceback:" % type) 48 | (type, value, tb) = sys.exc_info () 49 | extxt = traceback.format_exception_only (type, value) 50 | for line in traceback.format_tb(tb): 51 | debugprint (line.strip ()) 52 | debugprint (extxt[0].strip ()) 53 | debugprint (end) 54 | set_debugging (d) 55 | -------------------------------------------------------------------------------- /dnssdresolve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import dbus, re 22 | import urllib.parse 23 | from debug import * 24 | 25 | class DNSSDHostNamesResolver: 26 | def __init__ (self, devices): 27 | self._devices = devices 28 | self._unresolved = len (devices) 29 | self._device_uri_by_name = {} 30 | debugprint ("+%s" % self) 31 | 32 | def __del__ (self): 33 | debugprint ("-%s" % self) 34 | 35 | def resolve (self, reply_handler): 36 | 37 | self._reply_handler = reply_handler 38 | 39 | bus = dbus.SystemBus () 40 | if not bus: 41 | reply_handler ([]) 42 | del self._devices 43 | del self._reply_handler 44 | return 45 | 46 | for uri, device in self._devices.items (): 47 | if not uri.startswith ("dnssd://"): 48 | self._unresolved -= 1 49 | continue 50 | 51 | # We need to resolve the DNS-SD hostname in order to 52 | # compare with other network devices. 53 | result = urllib.parse.urlparse (uri) 54 | hostname = result.netloc 55 | elements = hostname.rsplit (".", 3) 56 | if len (elements) != 4: 57 | self._resolved () 58 | continue 59 | 60 | name, stype, protocol, domain = elements 61 | name = urllib.parse.unquote (name) 62 | stype += "." + protocol # e.g. _printer._tcp 63 | 64 | try: 65 | obj = bus.get_object ("org.freedesktop.Avahi", "/") 66 | server = dbus.Interface (obj, 67 | "org.freedesktop.Avahi.Server") 68 | self._device_uri_by_name[(name, stype, domain)] = uri 69 | debugprint ("Resolving address for %s" % hostname) 70 | server.ResolveService (-1, -1, 71 | name, stype, domain, 72 | -1, 0, 73 | reply_handler=self._reply, 74 | error_handler=lambda e: 75 | self._error (uri, e)) 76 | except dbus.DBusException as e: 77 | debugprint ("Failed to resolve address: %s" % repr (e)) 78 | self._resolved () 79 | 80 | def _resolved (self): 81 | self._unresolved -= 1 82 | if self._unresolved == 0: 83 | debugprint ("All addresses resolved") 84 | self._reply_handler (self._devices) 85 | del self._devices 86 | del self._reply_handler 87 | 88 | def _reply (self, interface, protocol, name, stype, domain, 89 | host, aprotocol, address, port, txt, flags): 90 | uri = self._device_uri_by_name[(name, stype, domain)] 91 | self._devices[uri].address = address 92 | hostname = host 93 | p = hostname.find(".") 94 | if p != -1: 95 | hostname = hostname[:p] 96 | debugprint ("%s is at %s (%s)" % (uri, address, hostname)) 97 | self._devices[uri].hostname = hostname 98 | self._resolved () 99 | 100 | def _error (self, uri, error): 101 | debugprint ("Error resolving %s: %s" % (uri, repr (error))) 102 | self._resolved () 103 | 104 | if __name__ == '__main__': 105 | class Device: 106 | def __repr__ (self): 107 | try: 108 | return "" % self.address 109 | except: 110 | return "" 111 | 112 | devices = {"dnssd://dlk-08E206-P1._printer._tcp.local/": Device(), 113 | "dnssd://foo._printer._tcp.local/": Device()} 114 | from dbus.glib import DBusGMainLoop 115 | DBusGMainLoop (set_as_default=True) 116 | 117 | class Test: 118 | def __init__ (self, loop, devices): 119 | self._loop = loop 120 | self._devices = devices 121 | 122 | def run (self): 123 | r = DNSSDHostNamesResolver (self._devices) 124 | r.resolve (reply_handler=self.reply) 125 | return False 126 | 127 | def reply (self, *args): 128 | print(args) 129 | self._loop.quit () 130 | 131 | from gi.repository import GObject 132 | from gi.repository import GLib 133 | loop = GObject.MainLoop () 134 | set_debugging (True) 135 | GLib.idle_add (Test (loop, devices).run) 136 | loop.run () 137 | -------------------------------------------------------------------------------- /errordialogs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2006, 2007, 2008, 2010, 2013 Red Hat, Inc. 6 | ## Authors: 7 | ## Florian Festi 8 | ## Tim Waugh 9 | 10 | ## This program is free software; you can redistribute it and/or modify 11 | ## it under the terms of the GNU General Public License as published by 12 | ## the Free Software Foundation; either version 2 of the License, or 13 | ## (at your option) any later version. 14 | 15 | ## This program is distributed in the hope that it will be useful, 16 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ## GNU General Public License for more details. 19 | 20 | ## You should have received a copy of the GNU General Public License 21 | ## along with this program; if not, write to the Free Software 22 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | import config 25 | import cups 26 | from gi.repository import Gtk 27 | import gettext 28 | gettext.install(domain=config.PACKAGE, localedir=config.localedir) 29 | 30 | def show_dialog (title, text, type, parent=None): 31 | dialog = Gtk.MessageDialog (parent=parent, 32 | modal=True, destroy_with_parent=True, 33 | message_type=type, 34 | buttons=Gtk.ButtonsType.OK, 35 | text=title) 36 | dialog.format_secondary_text (text) 37 | dialog.run () 38 | dialog.destroy () 39 | 40 | def show_info_dialog (title, text, parent=None): 41 | return show_dialog (title, text, Gtk.MessageType.INFO, parent=parent) 42 | 43 | def show_error_dialog (title, text, parent=None): 44 | return show_dialog (title, text, Gtk.MessageType.ERROR, parent=parent) 45 | 46 | def show_IPP_Error(exception, message, parent=None): 47 | if exception == 0: 48 | # In this case, the user has canceled an authentication dialog. 49 | return 50 | elif exception == cups.IPP_SERVICE_UNAVAILABLE: 51 | # In this case, the user has canceled a retry dialog. 52 | return 53 | else: 54 | title = _("CUPS server error") 55 | text = _("There was an error during the CUPS " 56 | "operation: '%s'.") % message 57 | 58 | show_error_dialog (title, text, parent) 59 | 60 | def show_HTTP_Error(status, parent=None): 61 | if (status == cups.HTTP_UNAUTHORIZED or 62 | status == cups.HTTP_FORBIDDEN): 63 | title = _('Not authorized') 64 | text = (_('The password may be incorrect, or the ' 65 | 'server may be configured to deny ' 66 | 'remote administration.')) 67 | else: 68 | title = _('CUPS server error') 69 | if status == cups.HTTP_BAD_REQUEST: 70 | msg = _("Bad request") 71 | elif status == cups.HTTP_NOT_FOUND: 72 | msg = _("Not found") 73 | elif status == cups.HTTP_REQUEST_TIMEOUT: 74 | msg = _("Request timeout") 75 | elif status == cups.HTTP_UPGRADE_REQUIRED: 76 | msg = _("Upgrade required") 77 | elif status == cups.HTTP_SERVER_ERROR: 78 | msg = _("Server error") 79 | elif status == -1: 80 | msg = _("Not connected") 81 | else: 82 | msg = _("status %s") % status 83 | 84 | text = _("There was an HTTP error: %s.") % msg 85 | 86 | show_error_dialog (title, text, parent) 87 | -------------------------------------------------------------------------------- /gitlog-to-changelog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Convert git log output to ChangeLog format. 3 | 4 | my $VERSION = '2009-06-04 08:53'; # UTC 5 | # The definition above must lie within the first 8 lines in order 6 | # for the Emacs time-stamp write hook (at end) to update it. 7 | # If you change this file with Emacs, please let the write hook 8 | # do its job. Otherwise, update this string manually. 9 | 10 | # Copyright (C) 2008, 2009 Free Software Foundation, Inc. 11 | 12 | # This program is free software: you can redistribute it and/or modify 13 | # it under the terms of the GNU General Public License as published by 14 | # the Free Software Foundation, either version 3 of the License, or 15 | # (at your option) any later version. 16 | 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU General Public License for more details. 21 | 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | 25 | # Written by Jim Meyering 26 | 27 | use strict; 28 | use warnings; 29 | use Getopt::Long; 30 | use POSIX qw(strftime); 31 | 32 | (my $ME = $0) =~ s|.*/||; 33 | 34 | # use File::Coda; # http://meyering.net/code/Coda/ 35 | END { 36 | defined fileno STDOUT or return; 37 | close STDOUT and return; 38 | warn "$ME: failed to close standard output: $!\n"; 39 | $? ||= 1; 40 | } 41 | 42 | sub usage ($) 43 | { 44 | my ($exit_code) = @_; 45 | my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); 46 | if ($exit_code != 0) 47 | { 48 | print $STREAM "Try `$ME --help' for more information.\n"; 49 | } 50 | else 51 | { 52 | print $STREAM < ChangeLog 70 | $ME -- -n 5 foo > last-5-commits-to-branch-foo 71 | 72 | EOF 73 | } 74 | exit $exit_code; 75 | } 76 | 77 | # If the string $S is a well-behaved file name, simply return it. 78 | # If it contains white space, quotes, etc., quote it, and return the new string. 79 | sub shell_quote($) 80 | { 81 | my ($s) = @_; 82 | if ($s =~ m![^\w+/.,-]!) 83 | { 84 | # Convert each single quote to '\'' 85 | $s =~ s/\'/\'\\\'\'/g; 86 | # Then single quote the string. 87 | $s = "'$s'"; 88 | } 89 | return $s; 90 | } 91 | 92 | sub quoted_cmd(@) 93 | { 94 | return join (' ', map {shell_quote $_} @_); 95 | } 96 | 97 | { 98 | my $since_date = '1970-01-01 UTC'; 99 | GetOptions 100 | ( 101 | help => sub { usage 0 }, 102 | version => sub { print "$ME version $VERSION\n"; exit }, 103 | 'since=s' => \$since_date, 104 | ) or usage 1; 105 | 106 | my @cmd = (qw (git log --log-size), "--since=$since_date", 107 | '--pretty=format:%ct %an <%ae>%n%n%s%n%b%n', @ARGV); 108 | open PIPE, '-|', @cmd 109 | or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n" 110 | . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); 111 | 112 | my $prev_date_line = ''; 113 | while (1) 114 | { 115 | defined (my $in = ) 116 | or last; 117 | $in =~ /^log size (\d+)$/ 118 | or die "$ME:$.: Invalid line (expected log size):\n$in"; 119 | my $log_nbytes = $1; 120 | 121 | my $log; 122 | my $n_read = read PIPE, $log, $log_nbytes; 123 | $n_read == $log_nbytes 124 | or die "$ME:$.: unexpected EOF\n"; 125 | 126 | my @line = split "\n", $log; 127 | my $author_line = shift @line; 128 | defined $author_line 129 | or die "$ME:$.: unexpected EOF\n"; 130 | $author_line =~ /^(\d+) (.*>)$/ 131 | or die "$ME:$.: Invalid line " 132 | . "(expected date/author/email):\n$author_line\n"; 133 | 134 | my $date_line = sprintf "%s $2\n", strftime ("%F", localtime ($1)); 135 | # If this line would be the same as the previous date/name/email 136 | # line, then arrange not to print it. 137 | if ($date_line ne $prev_date_line) 138 | { 139 | $prev_date_line eq '' 140 | or print "\n"; 141 | print $date_line; 142 | } 143 | $prev_date_line = $date_line; 144 | 145 | # Omit "Signed-off-by..." lines. 146 | @line = grep !/^Signed-off-by: .*>$/, @line; 147 | 148 | # If there were any lines 149 | if (@line == 0) 150 | { 151 | warn "$ME: warning: empty commit message:\n $date_line\n"; 152 | } 153 | else 154 | { 155 | # Remove leading and trailing blank lines. 156 | while ($line[0] =~ /^\s*$/) { shift @line; } 157 | while ($line[$#line] =~ /^\s*$/) { pop @line; } 158 | 159 | # Prefix each non-empty line with a TAB. 160 | @line = map { length $_ ? "\t$_" : '' } @line; 161 | 162 | print "\n", join ("\n", @line), "\n"; 163 | } 164 | 165 | defined ($in = ) 166 | or last; 167 | $in ne "\n" 168 | and die "$ME:$.: unexpected line:\n$in"; 169 | } 170 | 171 | close PIPE 172 | or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; 173 | # FIXME-someday: include $PROCESS_STATUS in the diagnostic 174 | } 175 | 176 | # Local Variables: 177 | # indent-tabs-mode: nil 178 | # eval: (add-hook 'write-file-hooks 'time-stamp) 179 | # time-stamp-start: "my $VERSION = '" 180 | # time-stamp-format: "%:y-%02m-%02d %02H:%02M" 181 | # time-stamp-time-zone: "UTC" 182 | # time-stamp-end: "'; # UTC" 183 | # End: 184 | -------------------------------------------------------------------------------- /gtkinklevel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2009, 2010, 2012 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | from gi.repository import Gdk 22 | from gi.repository import Gtk 23 | import cairo 24 | 25 | class GtkInkLevel (Gtk.DrawingArea): 26 | def __init__ (self, color, level=0): 27 | Gtk.DrawingArea.__init__ (self) 28 | self.connect ('draw', self.draw) 29 | self._level = level 30 | self._color = None 31 | if color: 32 | self._color = Gdk.color_parse (color) 33 | if not self._color: 34 | self._color = Gdk.color_parse ('#cccccc') 35 | 36 | self.set_size_request (30, 45) 37 | 38 | def set_level (self, level): 39 | self._level = level 40 | self.queue_resize () 41 | 42 | def get_level (self): 43 | return self._level 44 | 45 | def draw (self, widget, ctx): 46 | w = widget.get_allocated_width () 47 | h = widget.get_allocated_height () 48 | ratio = 1.0 * h / w 49 | if ratio < 1.5: 50 | w = h * 2.0 / 3.0 51 | else: 52 | h = w * 3.0 / 2.0 53 | thickness = 1 54 | ctx.translate (thickness, thickness) 55 | ctx.scale (w - 2 * thickness, h - 2 * thickness) 56 | thickness = max (ctx.device_to_user_distance (thickness, thickness)) 57 | 58 | r = self._color.red / 65535.0 59 | g = self._color.green / 65535.0 60 | b = self._color.blue / 65535.0 61 | fill_point = self._level / 100.0 62 | 63 | ctx.move_to (0.5, 0.0) 64 | ctx.curve_to (0.5, 0.33, 1.0, 0.5, 1.0, 0.67) 65 | ctx.curve_to (1.0, 0.85, 0.85, 1.0, 0.5, 1.0) 66 | ctx.curve_to (0.15, 1.0, 0.0, 0.85, 0.0, 0.67) 67 | ctx.curve_to (0.0, 0.5, 0.1, 0.2, 0.5, 0.0) 68 | ctx.close_path () 69 | ctx.set_source_rgb (r, g, b) 70 | ctx.set_line_width (thickness) 71 | ctx.stroke_preserve () 72 | if fill_point > 0.0: 73 | grad_width = 0.10 74 | grad_start = fill_point - (grad_width / 2) 75 | if grad_start < 0: 76 | grad_start = 0 77 | 78 | pat = cairo.LinearGradient (0, 1, 0, 0) 79 | pat.add_color_stop_rgba (0, r, g, b, 1) 80 | pat.add_color_stop_rgba ((self._level - 5) / 100.0, r, g, b, 1) 81 | pat.add_color_stop_rgba ((self._level + 5)/ 100.0, 1, 1, 1, 1) 82 | pat.add_color_stop_rgba (1.0, 1, 1, 1, 1) 83 | ctx.set_source (pat) 84 | ctx.fill () 85 | else: 86 | ctx.set_source_rgb (1, 1, 1) 87 | ctx.fill () 88 | 89 | ctx.set_line_width (thickness / 2) 90 | 91 | ctx.move_to (0.5, 0.0) 92 | ctx.line_to (0.5, 1.0) 93 | ctx.set_source_rgb (r, g, b) 94 | ctx.stroke () 95 | 96 | # 50% marker 97 | ctx.move_to (0.4, 0.5) 98 | ctx.line_to (0.6, 0.5) 99 | ctx.set_source_rgb (r, g, b) 100 | ctx.stroke () 101 | 102 | # 25% marker 103 | ctx.move_to (0.45, 0.75) 104 | ctx.line_to (0.55, 0.75) 105 | ctx.set_source_rgb (r, g, b) 106 | ctx.stroke () 107 | 108 | # 75% marker 109 | ctx.move_to (0.45, 0.25) 110 | ctx.line_to (0.55, 0.25) 111 | ctx.set_source_rgb (r, g, b) 112 | ctx.stroke () 113 | 114 | if __name__ == '__main__': 115 | # Try it out. 116 | from gi.repository import GLib 117 | import time 118 | def adjust_level (level): 119 | Gdk.threads_enter () 120 | l = level.get_level () 121 | l += 1 122 | if l > 100: 123 | l = 0 124 | level.set_level (l) 125 | Gdk.threads_leave () 126 | return True 127 | 128 | w = Gtk.Window () 129 | w.set_border_width (12) 130 | vbox = Gtk.VBox (spacing=6) 131 | w.add (vbox) 132 | hbox = Gtk.HBox (spacing=6) 133 | vbox.pack_start (hbox, False, False, 0) 134 | klevel = GtkInkLevel ("black", level=100) 135 | clevel = GtkInkLevel ("cyan", level=60) 136 | mlevel = GtkInkLevel ("magenta", level=30) 137 | ylevel = GtkInkLevel ("yellow", level=100) 138 | hbox.pack_start (klevel, False, False, 0) 139 | hbox.pack_start (clevel, False, False, 0) 140 | hbox.pack_start (mlevel, False, False, 0) 141 | hbox.pack_start (ylevel, False, False, 0) 142 | GLib.timeout_add (10, adjust_level, klevel) 143 | GLib.timeout_add (10, adjust_level, clevel) 144 | GLib.timeout_add (10, adjust_level, mlevel) 145 | GLib.timeout_add (10, adjust_level, ylevel) 146 | w.show_all () 147 | w.connect ('delete_event', Gtk.main_quit) 148 | Gdk.threads_init () 149 | Gtk.main () 150 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012 Red Hat, Inc. 6 | ## Authors: 7 | ## Florian Festi 8 | ## Tim Waugh 9 | 10 | ## This program is free software; you can redistribute it and/or modify 11 | ## it under the terms of the GNU General Public License as published by 12 | ## the Free Software Foundation; either version 2 of the License, or 13 | ## (at your option) any later version. 14 | 15 | ## This program is distributed in the hope that it will be useful, 16 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ## GNU General Public License for more details. 19 | 20 | ## You should have received a copy of the GNU General Public License 21 | ## along with this program; if not, write to the Free Software 22 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | from gi.repository import GObject 25 | from gi.repository import Gtk 26 | import os 27 | 28 | import config 29 | pkgdata = config.pkgdatadir 30 | 31 | class GtkGUI(GObject.GObject): 32 | def getWidgets(self, widgets, domain=None): 33 | ui_dir = os.environ.get ("SYSTEM_CONFIG_PRINTER_UI", 34 | os.path.join (pkgdata, "ui")) 35 | self._bld = [] 36 | for xmlfile, names in widgets.items (): 37 | bld = Gtk.Builder () 38 | self._bld.append (bld) 39 | 40 | if domain: 41 | bld.set_translation_domain (domain) 42 | 43 | bld.add_from_file (os.path.join (ui_dir, xmlfile + ".ui")) 44 | for name in names: 45 | widget = bld.get_object(name) 46 | if widget is None: 47 | raise ValueError("Widget '%s' not found" % name) 48 | setattr(self, name, widget) 49 | 50 | try: 51 | win = widget.get_top_level() 52 | except AttributeError: 53 | win = None 54 | 55 | if win is not None: 56 | Gtk.Window.set_focus_on_map(widget.get_top_level (), 57 | self.focus_on_map) 58 | widget.show() 59 | 60 | def connect_signals (self): 61 | for bld in self._bld: 62 | bld.connect_signals (self) 63 | -------------------------------------------------------------------------------- /icons/i-network-printer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenPrinting/system-config-printer/1f7c8095c1b3c64a9fbe348a53df4bc74625f3ee/icons/i-network-printer.png -------------------------------------------------------------------------------- /install-printerdriver.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/install-printerdriver.py "$@" 4 | -------------------------------------------------------------------------------- /install-printerdriver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import gi 4 | gi.require_version('PackageKitGlib', '1.0') 5 | from gi.repository import GLib, PackageKitGlib 6 | import sys 7 | from debug import * 8 | 9 | # progress callback 10 | # http://www.packagekit.org/gtk-doc/PkProgress.html 11 | def progress(progress, type, user_data): 12 | if (type.value_name == "PK_PROGRESS_TYPE_PERCENTAGE" and 13 | progress.props.package is not None): 14 | sys.stdout.write ("P%d\n" % progress.props.percentage) 15 | sys.stdout.flush () 16 | else: 17 | sys.stdout.write ("P%d\n" % -10) 18 | sys.stdout.flush () 19 | 20 | set_debugging (True) 21 | 22 | try: 23 | package = sys.argv[1] 24 | except: 25 | debugprint("Missing package name to install.") 26 | sys.exit(1) 27 | 28 | try: 29 | repo = sys.argv[2] 30 | except: 31 | debugprint("Missing name of repo.") 32 | sys.exit(1) 33 | 34 | try: 35 | repo_gpg_id = sys.argv[3] 36 | except: 37 | repo_gpg_id = None 38 | 39 | # get PackageKit client 40 | pk = PackageKitGlib.Client() 41 | 42 | refresh_cache_needed = False 43 | 44 | # install repository key 45 | if repo_gpg_id: 46 | debugprint("Signature key supplied") 47 | debugprint("pk.install_signature") 48 | try: 49 | res = pk.install_signature(PackageKitGlib.SigTypeEnum.GPG, repo_gpg_id, 50 | '', None, progress, None) 51 | refresh_cache_needed = True 52 | debugprint("pk.install_signature succeeded") 53 | except GLib.GError: 54 | debugprint("pk.install_signature failed") 55 | sys.exit(1) 56 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 57 | debugprint("pk.install_signature errored") 58 | sys.exit(1) 59 | 60 | # check if we already have the package installed or available 61 | debugprint("pk.resolve") 62 | try: 63 | res = pk.resolve(PackageKitGlib.FilterEnum.NONE, [package], 64 | None, progress, None) 65 | repo_enable_needed = False 66 | debugprint("pk.resolve succeeded") 67 | except GLib.GError: 68 | repo_enable_needed = True 69 | debugprint("pk.resolve failed") 70 | package_ids = res.get_package_array() 71 | if len(package_ids) <= 0: 72 | debugprint("res.get_package_array() failed") 73 | repo_enable_needed = True 74 | 75 | if repo_enable_needed: 76 | # Cannot resolve, so we need to install the repo 77 | # add repository; see 78 | # http://www.packagekit.org/gtk-doc/PackageKit-pk-client-sync.html#pk-client-repo-enable 79 | debugprint("pk.repo_enable") 80 | try: 81 | res = pk.repo_enable(repo, True, None, progress, None) 82 | refresh_cache_needed = True 83 | debugprint("pk.repo_enable succeeded") 84 | except GLib.GError: 85 | debugprint("pk.repo_enable failed") 86 | sys.exit(1) 87 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 88 | debugprint("pk.repo_enable errored") 89 | sys.exit(1) 90 | 91 | if refresh_cache_needed: 92 | # download/update the indexes 93 | debugprint("pk.refresh_cache") 94 | try: 95 | res = pk.refresh_cache(False, None, progress, None) 96 | debugprint("pk.refresh_cache succeeded") 97 | except GLib.GError: 98 | debugprint("pk.refresh_cache failed") 99 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 100 | debugprint("pk.refresh_cache errored") 101 | 102 | # map package name to PackageKit ID; do not print progress here, it's fast 103 | debugprint("pk.resolve") 104 | try: 105 | res = pk.resolve(PackageKitGlib.FilterEnum.NONE, [package], 106 | None, progress, None) 107 | debugprint("pk.resolve succeeded") 108 | except GLib.GError: 109 | debugprint("pk.resolve failed") 110 | sys.exit(1) 111 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 112 | debugprint("pk.resolve errored") 113 | sys.exit(1) 114 | package_ids = res.get_package_array() 115 | if len(package_ids) <= 0: 116 | debugprint("res.get_package_array() failed") 117 | sys.exit(1) 118 | package_id = package_ids[0].get_id() 119 | debugprint("package_id: %s" % package_id) 120 | 121 | # install the first match, unless already installed 122 | if package_ids[0].get_info() & PackageKitGlib.InfoEnum.INSTALLED == 0: 123 | debugprint("package not installed") 124 | debugprint("pk.install_packages") 125 | # install package 126 | if repo_gpg_id: 127 | debugprint("Signature key supplied") 128 | repo_gpg_id_supplied = True 129 | else: 130 | debugprint("Signature key not supplied") 131 | repo_gpg_id_supplied = False 132 | try: 133 | res = pk.install_packages(repo_gpg_id_supplied, [package_id], None, 134 | progress, None) 135 | debugprint("pk.install_packages succeeded") 136 | except GLib.GError: 137 | debugprint("pk.install_packages failed, retrying with modified package ID") 138 | # See aptdaemon Ubuntu bug #1397750. 139 | try: 140 | # Remove last element of the package ID, after the last ";" 141 | package_id_mod = package_id[:package_id.rfind(";")+1] 142 | res = pk.install_packages(repo_gpg_id_supplied, [package_id_mod], 143 | None, progress, None) 144 | debugprint("pk.install_packages succeeded") 145 | except GLib.GError: 146 | debugprint("pk.install_packages failed") 147 | sys.exit(1) 148 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 149 | debugprint("pk.install_packages errored") 150 | sys.exit(1) 151 | 152 | debugprint("Package successfully installed") 153 | # If we reach this point, the requested package is on the system, either 154 | # because we have installed it now or because it was already there 155 | 156 | # Return the list of files contained in the package 157 | try: 158 | res = pk.get_files([package_id], None, progress, None) 159 | except GLib.GError: 160 | pass 161 | 162 | files = res.get_files_array() 163 | if files: 164 | for f in files[0].get_property('files'): 165 | print(f) 166 | 167 | # Tell the caller that we are done 168 | print("done") 169 | -------------------------------------------------------------------------------- /installpackage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2008, 2009, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import os 23 | import dbus 24 | import dbus.glib 25 | from gi.repository import GLib 26 | from debug import * 27 | 28 | class PackageKit: 29 | DBUS_NAME="org.freedesktop.PackageKit" 30 | DBUS_PATH="/org/freedesktop/PackageKit" 31 | DBUS_IFACE="org.freedesktop.PackageKit.Modify" 32 | 33 | def __init__ (self): 34 | try: 35 | bus = dbus.SessionBus () 36 | remote_object = bus.get_object(self.DBUS_NAME, self.DBUS_PATH) 37 | iface = dbus.Interface(remote_object, self.DBUS_IFACE) 38 | except dbus.exceptions.DBusException: 39 | # System bus not running. 40 | iface = None 41 | self.iface = iface 42 | 43 | def InstallPackageName (self, xid, timestamp, name): 44 | try: 45 | if self.iface is not None: 46 | self.iface.InstallPackageNames(xid, [name], 47 | "show-progress,show-finished,show-warning", 48 | timeout = 999999) 49 | except dbus.exceptions.DBusException: 50 | pass 51 | 52 | def InstallProvideFile (self, xid, timestamp, filename): 53 | try: 54 | if self.iface is not None: 55 | self.iface.InstallProvideFiles(xid, [filename], 56 | "show-progress,show-finished,show-warning", 57 | timeout = 999999) 58 | except dbus.exceptions.DBusException: 59 | pass 60 | -------------------------------------------------------------------------------- /killtimer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | import threading 24 | 25 | from gi.repository import GLib 26 | 27 | from debug import * 28 | 29 | class KillTimer: 30 | def __init__ (self, timeout=30, killfunc=None): 31 | self._timeout = timeout 32 | self._killfunc = killfunc 33 | self._holds = 0 34 | self._add_timeout () 35 | self._lock = threading.Lock() 36 | 37 | def _add_timeout (self): 38 | self._timer = GLib.timeout_add_seconds (self._timeout, self._kill) 39 | 40 | def _kill (self): 41 | debugprint ("Timeout (%ds), exiting" % self._timeout) 42 | if self._killfunc: 43 | self._killfunc () 44 | else: 45 | sys.exit (0) 46 | 47 | def add_hold (self): 48 | self._lock.acquire() 49 | if self._holds == 0: 50 | debugprint ("Kill timer stopped") 51 | GLib.source_remove (self._timer) 52 | 53 | self._holds += 1 54 | self._lock.release() 55 | 56 | def remove_hold (self): 57 | self._lock.acquire() 58 | if self._holds > 0: 59 | self._holds -= 1 60 | if self._holds == 0: 61 | debugprint ("Kill timer started") 62 | self._add_timeout () 63 | self._lock.release() 64 | 65 | def alive (self): 66 | self._lock.acquire() 67 | if self._holds == 0: 68 | GLib.source_remove (self._timer) 69 | self._add_timeout () 70 | self._lock.release() 71 | -------------------------------------------------------------------------------- /man/system-config-printer.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | system-config-printer 7 | 8 | Tim 9 | Waugh 10 | 11 |
twaugh@redhat.com
12 |
13 |
14 |
15 | 16 | 17 | Man pages 18 | 19 | 20 | 21 | system-config-printer 22 | 25 Apr 2013 23 | 24 | 25 | 26 | system-config-printer 27 | 1 28 | 29 | 30 | 31 | system-config-printer 32 | configure a CUPS server 33 | 34 | 35 | 36 | 37 | system-config-printer 38 | 39 | --show-jobs printer 40 | --debug 41 | --help 42 | 43 | 44 | 45 | 46 | 47 | Description 48 | 49 | system-config-printer configures a 50 | CUPS server. It uses the CUPS API (bound to Python with 51 | pycups) to do this. The communication with the server is 52 | performed using IPP. As a result, it is equally able to 53 | configure a remote CUPS server as a local one. 54 | 55 | 56 | 57 | Options 58 | 59 | 60 | 61 | 62 | 63 | Display a short usage message. 64 | 65 | 66 | 67 | 68 | 69 | 70 | Show the named print queue. 71 | 72 | 73 | 74 | 75 | 76 | 77 | Enable debugging output. 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | system-config-printer 87 | 24 Nov 2010 88 | 89 | 90 | 91 | system-config-printer-applet 92 | 1 93 | 94 | 95 | 96 | system-config-printer-applet 97 | print job manager 98 | 99 | 100 | 101 | 102 | system-config-printer-applet 103 | --help 104 | --version 105 | --debug 106 | 107 | 108 | 109 | 110 | Description 111 | 112 | system-config-printer-applet is a 113 | print job manager for CUPS. Normally it will display a 114 | printer icon in the notification area, greyed out when there 115 | are no print jobs for the current user. Clicking on the icon 116 | displays a simple print job manager for cancelling or 117 | reprinting jobs. 118 | 119 | To save memory, the applet waits first of all until the 120 | user has printed a job before putting the icon in the 121 | notification area. To invoke the print job manager before a 122 | job has been printed, run the applet with the 123 | option: a desktop file is 124 | provided for this, so that it should appear in the system 125 | menu. 126 | 127 | As well as displaying a printer icon in the notification 128 | area, the applet also provides a D-BUS server for the 129 | com.redhat.PrintDriverSelection interface, to help configure a 130 | new printer when it is plugged in. 131 | 132 | 133 | 134 | Options 135 | 136 | 137 | 138 | 139 | 140 | Display a short usage message. 141 | 142 | 143 | 144 | 145 | 146 | 147 | Display the version of the applet. 148 | 149 | 150 | 151 | 152 | 153 | 154 | Show debugging information. 155 | 156 | 157 | 158 | 159 | 160 | 161 |
162 | -------------------------------------------------------------------------------- /mkinstalldirs: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # mkinstalldirs --- make directory hierarchy 3 | 4 | scriptversion=2009-04-28.21; # UTC 5 | 6 | # Original author: Noah Friedman 7 | # Created: 1993-05-16 8 | # Public domain. 9 | # 10 | # This file is maintained in Automake, please report 11 | # bugs to or send patches to 12 | # . 13 | 14 | nl=' 15 | ' 16 | IFS=" "" $nl" 17 | errstatus=0 18 | dirmode= 19 | 20 | usage="\ 21 | Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... 22 | 23 | Create each directory DIR (with mode MODE, if specified), including all 24 | leading file name components. 25 | 26 | Report bugs to ." 27 | 28 | # process command line arguments 29 | while test $# -gt 0 ; do 30 | case $1 in 31 | -h | --help | --h*) # -h for help 32 | echo "$usage" 33 | exit $? 34 | ;; 35 | -m) # -m PERM arg 36 | shift 37 | test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } 38 | dirmode=$1 39 | shift 40 | ;; 41 | --version) 42 | echo "$0 $scriptversion" 43 | exit $? 44 | ;; 45 | --) # stop option processing 46 | shift 47 | break 48 | ;; 49 | -*) # unknown option 50 | echo "$usage" 1>&2 51 | exit 1 52 | ;; 53 | *) # first non-opt arg 54 | break 55 | ;; 56 | esac 57 | done 58 | 59 | for file 60 | do 61 | if test -d "$file"; then 62 | shift 63 | else 64 | break 65 | fi 66 | done 67 | 68 | case $# in 69 | 0) exit 0 ;; 70 | esac 71 | 72 | # Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and 73 | # mkdir -p a/c at the same time, both will detect that a is missing, 74 | # one will create a, then the other will try to create a and die with 75 | # a "File exists" error. This is a problem when calling mkinstalldirs 76 | # from a parallel make. We use --version in the probe to restrict 77 | # ourselves to GNU mkdir, which is thread-safe. 78 | case $dirmode in 79 | '') 80 | if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then 81 | echo "mkdir -p -- $*" 82 | exec mkdir -p -- "$@" 83 | else 84 | # On NextStep and OpenStep, the 'mkdir' command does not 85 | # recognize any option. It will interpret all options as 86 | # directories to create, and then abort because '.' already 87 | # exists. 88 | test -d ./-p && rmdir ./-p 89 | test -d ./--version && rmdir ./--version 90 | fi 91 | ;; 92 | *) 93 | if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && 94 | test ! -d ./--version; then 95 | echo "mkdir -m $dirmode -p -- $*" 96 | exec mkdir -m "$dirmode" -p -- "$@" 97 | else 98 | # Clean up after NextStep and OpenStep mkdir. 99 | for d in ./-m ./-p ./--version "./$dirmode"; 100 | do 101 | test -d $d && rmdir $d 102 | done 103 | fi 104 | ;; 105 | esac 106 | 107 | for file 108 | do 109 | case $file in 110 | /*) pathcomp=/ ;; 111 | *) pathcomp= ;; 112 | esac 113 | oIFS=$IFS 114 | IFS=/ 115 | set fnord $file 116 | shift 117 | IFS=$oIFS 118 | 119 | for d 120 | do 121 | test "x$d" = x && continue 122 | 123 | pathcomp=$pathcomp$d 124 | case $pathcomp in 125 | -*) pathcomp=./$pathcomp ;; 126 | esac 127 | 128 | if test ! -d "$pathcomp"; then 129 | echo "mkdir $pathcomp" 130 | 131 | mkdir "$pathcomp" || lasterr=$? 132 | 133 | if test ! -d "$pathcomp"; then 134 | errstatus=$lasterr 135 | else 136 | if test ! -z "$dirmode"; then 137 | echo "chmod $dirmode $pathcomp" 138 | lasterr= 139 | chmod "$dirmode" "$pathcomp" || lasterr=$? 140 | 141 | if test ! -z "$lasterr"; then 142 | errstatus=$lasterr 143 | fi 144 | fi 145 | fi 146 | fi 147 | 148 | pathcomp=$pathcomp/ 149 | done 150 | done 151 | 152 | exit $errstatus 153 | 154 | # Local Variables: 155 | # mode: shell-script 156 | # sh-indentation: 2 157 | # eval: (add-hook 'write-file-hooks 'time-stamp) 158 | # time-stamp-start: "scriptversion=" 159 | # time-stamp-format: "%:y-%02m-%02d.%02H" 160 | # time-stamp-time-zone: "UTC" 161 | # time-stamp-end: "; # UTC" 162 | # End: 163 | -------------------------------------------------------------------------------- /po/ChangeLog: -------------------------------------------------------------------------------- 1 | 2006-06-19 gettextize 2 | 3 | * Makefile.in.in: New file, from gettext-0.14.5. 4 | * boldquot.sed: New file, from gettext-0.14.5. 5 | * en@boldquot.header: New file, from gettext-0.14.5. 6 | * en@quot.header: New file, from gettext-0.14.5. 7 | * insert-header.sin: New file, from gettext-0.14.5. 8 | * quot.sed: New file, from gettext-0.14.5. 9 | * remove-potcdate.sin: New file, from gettext-0.14.5. 10 | * Rules-quot: New file, from gettext-0.14.5. 11 | * POTFILES.in: New file. 12 | 13 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | ar 2 | as 3 | ast 4 | bg 5 | bn_IN 6 | bn 7 | br 8 | bs 9 | ca 10 | cs 11 | cy 12 | da 13 | de 14 | el 15 | en_GB 16 | es 17 | et 18 | fa 19 | fi 20 | fr 21 | fur 22 | gu 23 | he 24 | hi 25 | hr 26 | hu 27 | id 28 | is 29 | it 30 | ja 31 | ka 32 | kn 33 | ko 34 | lt 35 | lv 36 | mai 37 | ml 38 | mr 39 | ms 40 | nb 41 | nds 42 | nl 43 | nn 44 | oc 45 | or 46 | pa 47 | pl 48 | pt_BR 49 | pt 50 | ro 51 | ru 52 | si 53 | sk 54 | sl 55 | sr@latin 56 | sr 57 | sv 58 | ta 59 | te 60 | th 61 | tr 62 | uk 63 | vi 64 | zh_CN 65 | zh_TW 66 | -------------------------------------------------------------------------------- /po/Makevars: -------------------------------------------------------------------------------- 1 | # Makefile variables for PO directory in any package using GNU gettext. 2 | 3 | # Usually the message domain is the same as the package name. 4 | DOMAIN = $(PACKAGE) 5 | 6 | # These two variables depend on the location of this directory. 7 | subdir = po 8 | top_builddir = .. 9 | 10 | # These options get passed to xgettext. 11 | XGETTEXT_OPTIONS = --from-code=UTF-8 --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3 --add-comments 12 | 13 | # This is the copyright holder that gets inserted into the header of the 14 | # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding 15 | # package. (Note that the msgstr strings, extracted from the package's 16 | # sources, belong to the copyright holder of the package.) Translators are 17 | # expected to transfer the copyright for their translations to this person 18 | # or entity, or to disclaim their copyright. The empty string stands for 19 | # the public domain; in this case the translators are expected to disclaim 20 | # their copyright. 21 | COPYRIGHT_HOLDER = Red Hat 22 | 23 | # This is the email address or URL to which the translators shall report 24 | # bugs in the untranslated strings: 25 | # - Strings which are not entire sentences, see the maintainer guidelines 26 | # in the GNU gettext documentation, section 'Preparing Strings'. 27 | # - Strings which use unclear terms or require additional context to be 28 | # understood. 29 | # - Strings which make invalid assumptions about notation of date, time or 30 | # money. 31 | # - Pluralisation problems. 32 | # - Incorrect English spelling. 33 | # - Incorrect formatting. 34 | # It can be your email address, or a mailing list address where translators 35 | # can write to without being subscribed, or the URL of a web page through 36 | # which the translators can contact you. 37 | MSGID_BUGS_ADDRESS = https://bugzilla.redhat.com/bugzilla 38 | 39 | # This is the list of locale categories, beyond LC_MESSAGES, for which the 40 | # message catalogs shall be used. It is usually empty. 41 | EXTRA_LOCALE_CATEGORIES = 42 | 43 | # This tells whether or not to regenerate a PO file when $(DOMAIN).pot 44 | # has changed. Possible values are "yes" and "no". Set this to no if 45 | # the POT file is checked in the repository and the version control 46 | # program ignores timestamps. 47 | PO_DEPENDS_ON_POT = no 48 | 49 | # This tells whether or not to forcibly update $(DOMAIN).pot and 50 | # regenerate PO files on "make dist". Possible values are "yes" and 51 | # "no". Set this to no if the POT file and PO files are maintained 52 | # externally. 53 | DIST_DEPENDS_ON_UPDATE_PO = no 54 | -------------------------------------------------------------------------------- /po/POTFILES.in: -------------------------------------------------------------------------------- 1 | # List of source files which contain translatable strings. 2 | # Please keep this file sorted alphabetically. 3 | 4 | applet.py 5 | asyncipp.py 6 | authconn.py 7 | errordialogs.py 8 | jobviewer.py 9 | newprinter.py 10 | options.py 11 | optionwidgets.py 12 | ppdippstr.py 13 | ppdsloader.py 14 | print-applet.desktop.in 15 | printerproperties.py 16 | probe_printer.py 17 | pysmb.py 18 | serversettings.py 19 | statereason.py 20 | system-config-printer.appdata.xml.in 21 | system-config-printer.desktop.in 22 | system-config-printer.py 23 | timedops.py 24 | ToolbarSearchEntry.py 25 | troubleshoot/base.py 26 | troubleshoot/CheckLocalServerPublishing.py 27 | troubleshoot/CheckNetworkServerSanity.py 28 | troubleshoot/CheckPPDSanity.py 29 | troubleshoot/CheckPrinterSanity.py 30 | troubleshoot/ChooseNetworkPrinter.py 31 | troubleshoot/ChoosePrinter.py 32 | troubleshoot/DeviceListed.py 33 | troubleshoot/ErrorLogCheckpoint.py 34 | troubleshoot/ErrorLogFetch.py 35 | troubleshoot/ErrorLogParse.py 36 | troubleshoot/__init__.py 37 | troubleshoot/Locale.py 38 | troubleshoot/LocalOrRemote.py 39 | troubleshoot/NetworkCUPSPrinterShared.py 40 | troubleshoot/PrinterStateReasons.py 41 | troubleshoot/PrintTestPage.py 42 | troubleshoot/QueueNotEnabled.py 43 | troubleshoot/QueueRejectingJobs.py 44 | troubleshoot/RemoteAddress.py 45 | troubleshoot/SchedulerNotRunning.py 46 | troubleshoot/ServerFirewalled.py 47 | troubleshoot/Shrug.py 48 | troubleshoot/Welcome.py 49 | ui/AboutDialog.ui 50 | ui/ConnectDialog.ui 51 | ui/ConnectingDialog.ui 52 | ui/InstallDialog.ui 53 | ui/JobsWindow.ui 54 | ui/NewPrinterName.ui 55 | ui/NewPrinterWindow.ui 56 | ui/PrinterPropertiesDialog.ui 57 | ui/PrintersWindow.ui 58 | ui/ServerSettingsDialog.ui 59 | ui/SMBBrowseDialog.ui 60 | ui/statusicon_popupmenu.ui 61 | ui/WaitWindow.ui 62 | -------------------------------------------------------------------------------- /po/Rules-quot: -------------------------------------------------------------------------------- 1 | # Special Makefile rules for English message catalogs with quotation marks. 2 | 3 | DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot 4 | 5 | .SUFFIXES: .insert-header .po-update-en 6 | 7 | en@quot.po-create: 8 | $(MAKE) en@quot.po-update 9 | en@boldquot.po-create: 10 | $(MAKE) en@boldquot.po-update 11 | 12 | en@quot.po-update: en@quot.po-update-en 13 | en@boldquot.po-update: en@boldquot.po-update-en 14 | 15 | .insert-header.po-update-en: 16 | @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ 17 | if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ 18 | tmpdir=`pwd`; \ 19 | echo "$$lang:"; \ 20 | ll=`echo $$lang | sed -e 's/@.*//'`; \ 21 | LC_ALL=C; export LC_ALL; \ 22 | cd $(srcdir); \ 23 | if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \ 24 | if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ 25 | rm -f $$tmpdir/$$lang.new.po; \ 26 | else \ 27 | if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ 28 | :; \ 29 | else \ 30 | echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ 31 | exit 1; \ 32 | fi; \ 33 | fi; \ 34 | else \ 35 | echo "creation of $$lang.po failed!" 1>&2; \ 36 | rm -f $$tmpdir/$$lang.new.po; \ 37 | fi 38 | 39 | en@quot.insert-header: insert-header.sin 40 | sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header 41 | 42 | en@boldquot.insert-header: insert-header.sin 43 | sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header 44 | 45 | mostlyclean: mostlyclean-quot 46 | mostlyclean-quot: 47 | rm -f *.insert-header 48 | -------------------------------------------------------------------------------- /po/boldquot.sed: -------------------------------------------------------------------------------- 1 | s/"\([^"]*\)"/“\1”/g 2 | s/`\([^`']*\)'/‘\1’/g 3 | s/ '\([^`']*\)' / ‘\1’ /g 4 | s/ '\([^`']*\)'$/ ‘\1’/g 5 | s/^'\([^`']*\)' /‘\1’ /g 6 | s/“”/""/g 7 | s/“/“/g 8 | s/”/”/g 9 | s/‘/‘/g 10 | s/’/’/g 11 | -------------------------------------------------------------------------------- /po/en@boldquot.header: -------------------------------------------------------------------------------- 1 | # All this catalog "translates" are quotation characters. 2 | # The msgids must be ASCII and therefore cannot contain real quotation 3 | # characters, only substitutes like grave accent (0x60), apostrophe (0x27) 4 | # and double quote (0x22). These substitutes look strange; see 5 | # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html 6 | # 7 | # This catalog translates grave accent (0x60) and apostrophe (0x27) to 8 | # left single quotation mark (U+2018) and right single quotation mark (U+2019). 9 | # It also translates pairs of apostrophe (0x27) to 10 | # left single quotation mark (U+2018) and right single quotation mark (U+2019) 11 | # and pairs of quotation mark (0x22) to 12 | # left double quotation mark (U+201C) and right double quotation mark (U+201D). 13 | # 14 | # When output to an UTF-8 terminal, the quotation characters appear perfectly. 15 | # When output to an ISO-8859-1 terminal, the single quotation marks are 16 | # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to 17 | # grave/acute accent (by libiconv), and the double quotation marks are 18 | # transliterated to 0x22. 19 | # When output to an ASCII terminal, the single quotation marks are 20 | # transliterated to apostrophes, and the double quotation marks are 21 | # transliterated to 0x22. 22 | # 23 | # This catalog furthermore displays the text between the quotation marks in 24 | # bold face, assuming the VT100/XTerm escape sequences. 25 | # 26 | -------------------------------------------------------------------------------- /po/en@quot.header: -------------------------------------------------------------------------------- 1 | # All this catalog "translates" are quotation characters. 2 | # The msgids must be ASCII and therefore cannot contain real quotation 3 | # characters, only substitutes like grave accent (0x60), apostrophe (0x27) 4 | # and double quote (0x22). These substitutes look strange; see 5 | # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html 6 | # 7 | # This catalog translates grave accent (0x60) and apostrophe (0x27) to 8 | # left single quotation mark (U+2018) and right single quotation mark (U+2019). 9 | # It also translates pairs of apostrophe (0x27) to 10 | # left single quotation mark (U+2018) and right single quotation mark (U+2019) 11 | # and pairs of quotation mark (0x22) to 12 | # left double quotation mark (U+201C) and right double quotation mark (U+201D). 13 | # 14 | # When output to an UTF-8 terminal, the quotation characters appear perfectly. 15 | # When output to an ISO-8859-1 terminal, the single quotation marks are 16 | # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to 17 | # grave/acute accent (by libiconv), and the double quotation marks are 18 | # transliterated to 0x22. 19 | # When output to an ASCII terminal, the single quotation marks are 20 | # transliterated to apostrophes, and the double quotation marks are 21 | # transliterated to 0x22. 22 | # 23 | -------------------------------------------------------------------------------- /po/insert-header.sin: -------------------------------------------------------------------------------- 1 | # Sed script that inserts the file called HEADER before the header entry. 2 | # 3 | # At each occurrence of a line starting with "msgid ", we execute the following 4 | # commands. At the first occurrence, insert the file. At the following 5 | # occurrences, do nothing. The distinction between the first and the following 6 | # occurrences is achieved by looking at the hold space. 7 | /^msgid /{ 8 | x 9 | # Test if the hold space is empty. 10 | s/m/m/ 11 | ta 12 | # Yes it was empty. First occurrence. Read the file. 13 | r HEADER 14 | # Output the file's contents by reading the next line. But don't lose the 15 | # current line while doing this. 16 | g 17 | N 18 | bb 19 | :a 20 | # The hold space was nonempty. Following occurrences. Do nothing. 21 | x 22 | :b 23 | } 24 | -------------------------------------------------------------------------------- /po/quot.sed: -------------------------------------------------------------------------------- 1 | s/"\([^"]*\)"/“\1”/g 2 | s/`\([^`']*\)'/‘\1’/g 3 | s/ '\([^`']*\)' / ‘\1’ /g 4 | s/ '\([^`']*\)'$/ ‘\1’/g 5 | s/^'\([^`']*\)' /‘\1’ /g 6 | s/“”/""/g 7 | -------------------------------------------------------------------------------- /po/remove-potcdate.sin: -------------------------------------------------------------------------------- 1 | # Sed script that remove the POT-Creation-Date line in the header entry 2 | # from a POT file. 3 | # 4 | # The distinction between the first and the following occurrences of the 5 | # pattern is achieved by looking at the hold space. 6 | /^"POT-Creation-Date: .*"$/{ 7 | x 8 | # Test if the hold space is empty. 9 | s/P/P/ 10 | ta 11 | # Yes it was empty. First occurrence. Remove the line. 12 | g 13 | d 14 | bb 15 | :a 16 | # The hold space was nonempty. Following occurrences. Do nothing. 17 | x 18 | :b 19 | } 20 | -------------------------------------------------------------------------------- /print-applet.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Print Queue Applet 3 | Comment=System tray icon for managing print jobs 4 | Exec=system-config-printer-applet 5 | Terminal=false 6 | Type=Application 7 | Icon=printer 8 | NotShowIn=KDE;GNOME;Cinnamon; 9 | StartupNotify=false 10 | X-GNOME-Autostart-Delay=30 11 | -------------------------------------------------------------------------------- /profile-ppds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import cups 3 | import cupshelpers 4 | import hotshot 5 | import hotshot.stats 6 | 7 | ppds = cupshelpers.ppds.PPDs (cups.Connection ().getPPDs ()) 8 | prof = hotshot.Profile ("a.prof") 9 | prof.runcall (lambda: ppds.getPPDNameFromDeviceID('','','')) 10 | prof.close () 11 | stats = hotshot.stats.load ("a.prof") 12 | stats.sort_stats ('time') 13 | stats.print_stats (100) 14 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "cupshelpers" 7 | version = "1.0" 8 | description = "Helper functions and classes for using CUPS" 9 | authors = [ 10 | { name = "Tim Waugh", email = "twaugh@redhat.com" }, 11 | { name = "Till Kamppeter", email = "till.kamppeter@gmail.com" }, 12 | { name = "Jiri Popelka", email = "jpopelka@redhat.com" }, 13 | { name = "Zdenek Dohnal", email = "zdohnal@redhat.com" }, 14 | ] 15 | license = { file = "COPYING" } 16 | readme = "README.md" 17 | classifiers = [ 18 | "Programming Language :: Python :: 3", 19 | "License :: OSI Approved :: GPL 2 or later", 20 | "Operating System :: Linux", 21 | ] 22 | requires-python = ">=3.7" 23 | 24 | [project.urls] 25 | "Homepage" = "https://github.com/OpenPrinting/system-config-printer" 26 | "Bug Tracker" = "https://github.com/OpenPrinting/system-config-printer/issues" 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2008 Red Hat, Inc. 4 | ## Copyright (C) 2008 Tim Waugh 5 | 6 | ## This program is free software; you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation; either version 2 of the License, or 9 | ## (at your option) any later version. 10 | 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program; if not, write to the Free Software 18 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | from distutils.core import setup 21 | setup(name='cupshelpers', 22 | version='1.0', 23 | description='Helper functions and classes for using CUPS', 24 | maintainer='Tim Waugh', 25 | maintainer_email='twaugh@redhat.com', 26 | packages=['cupshelpers']) 27 | -------------------------------------------------------------------------------- /smburi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012 Red Hat, Inc. 6 | ## Copyright (C) 2006, 2007 Florian Festi 7 | ## Copyright (C) 2006, 2007, 2008, 2009 Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | import urllib.parse 24 | 25 | def urlquote (x): 26 | q = urllib.parse.quote (x) 27 | for c in ["/", "@", ":"]: 28 | q = q.replace (c, "%%%02X" % ord (c)) 29 | 30 | return q 31 | 32 | class SMBURI: 33 | def __init__ (self, 34 | uri=None, 35 | group='', host='', share='', user='', password=''): 36 | if uri: 37 | if group or host or share or user or password: 38 | raise RuntimeError 39 | 40 | if uri.startswith ("smb://"): 41 | uri = uri[6:] 42 | 43 | self.uri = uri 44 | else: 45 | self.uri = self._construct (group, host, share, 46 | user=user, password=password) 47 | 48 | def _construct (self, group, host, share, user='', password=''): 49 | uri_password = '' 50 | if password: 51 | uri_password = ':' + urlquote (password) 52 | if user: 53 | uri_password += '@' 54 | uri = "%s%s%s" % (urlquote (user), 55 | uri_password, 56 | urlquote (group)) 57 | if len (group) > 0: 58 | uri += '/' 59 | uri += urlquote (host) 60 | if len (share) > 0: 61 | uri += "/" + urlquote (share) 62 | return uri 63 | 64 | def get_uri (self): 65 | return self.uri 66 | 67 | def sanitize_uri (self): 68 | group, host, share, user, password = self.separate () 69 | return self._construct (group, host, share) 70 | 71 | def separate (self): 72 | uri = self.get_uri () 73 | user = '' 74 | password = '' 75 | auth = uri.find ('@') 76 | if auth != -1: 77 | u = uri[:auth].find(':') 78 | if u != -1: 79 | user = uri[:u] 80 | password = uri[u + 1:auth] 81 | else: 82 | user = uri[:auth] 83 | uri = uri[auth + 1:] 84 | sep = uri.count ('/') 85 | group = '' 86 | if sep == 2: 87 | g = uri.find('/') 88 | group = uri[:g] 89 | uri = uri[g + 1:] 90 | if sep < 1: 91 | host = '' 92 | else: 93 | h = uri.find('/') 94 | host = uri[:h] 95 | uri = uri[h + 1:] 96 | p = host.find(':') 97 | if p != -1: 98 | host = host[:p] 99 | share = uri 100 | return (urllib.parse.unquote (group), urllib.parse.unquote (host), 101 | urllib.parse.unquote (share), 102 | urllib.parse.unquote (user), urllib.parse.unquote (password)) 103 | -------------------------------------------------------------------------------- /system-config-printer-applet.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/applet.py "$@" 4 | -------------------------------------------------------------------------------- /system-config-printer.appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | system-config-printer.desktop 4 | CC0 5 | GPL-2.0+ 6 | Print Settings 7 | Configure printer queues 8 | 9 | 10 |

With system-config-printer you can add, edit and delete 11 | printer queues. It allows you to choose the connection method and 12 | the printer driver.

13 | 14 |

For each queue, you can adjust the default page size and other 15 | driver options, as well as seeing ink/toner levels and status 16 | messages.

17 |
18 | 19 | 20 | 21 | Main window 22 | https://raw.githubusercontent.com/OpenPrinting/system-config-printer/master/data/screenshot-mainwindow.png 23 | 24 | 25 | Printer properties 26 | https://raw.githubusercontent.com/OpenPrinting/system-config-printer/master/data/screenshot-properties.png 27 | 28 | 29 | 30 | https://github.com/OpenPrinting/system-config-printer/ 31 | https://github.com/OpenPrinting/system-config-printer/issues 32 | 33 | 34 | system-config-printer 35 | org.fedoraproject.Config.Printing 36 | system-config-printer.desktop 37 | 38 | 39 | system-config-printer.desktop 40 | zdohnal@redhat.com 41 |
42 | -------------------------------------------------------------------------------- /system-config-printer.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Print Settings 3 | GenericName=Print Settings 4 | X-GNOME-FullName=Print Settings 5 | Comment=Configure printers 6 | Exec=system-config-printer 7 | Terminal=false 8 | Type=Application 9 | Icon=printer 10 | StartupNotify=true 11 | -------------------------------------------------------------------------------- /system-config-printer.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/system-config-printer.py "$@" 4 | -------------------------------------------------------------------------------- /test/test-cups-driver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- python -*- 3 | 4 | ## Copyright (C) 2008, 2014 Red Hat, Inc. 5 | ## Copyright (C) 2008 Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import sys 22 | 23 | import cups 24 | try: 25 | from cupshelpers import missingPackagesAndExecutables 26 | except ImportError: 27 | sys.path.append ('..') 28 | from cupshelpers import missingPackagesAndExecutables 29 | 30 | from getopt import getopt 31 | import os 32 | import posix 33 | import re 34 | import shlex 35 | import signal 36 | import subprocess 37 | import tempfile 38 | 39 | class TimedOut(Exception): 40 | def __init__ (self): 41 | Exception.__init__ (self, "Timed out") 42 | 43 | class MissingExecutables(Exception): 44 | def __init__ (self): 45 | Exception.__init__ (self, "Missing executables") 46 | 47 | class Driver: 48 | def __init__ (self, driver): 49 | self.exe = "/usr/lib/cups/driver/%s" % driver 50 | self.ppds = None 51 | self.files = {} 52 | signal.signal (signal.SIGALRM, self._alarm) 53 | 54 | def _alarm (self, sig, stack): 55 | raise TimedOut 56 | 57 | def list (self): 58 | if self.ppds: 59 | return self.ppds 60 | 61 | signal.alarm (60) 62 | p = subprocess.Popen ([self.exe, "list"], 63 | stdout=subprocess.PIPE, 64 | stderr=subprocess.PIPE) 65 | try: 66 | (stdout, stderr) = p.communicate () 67 | signal.alarm (0) 68 | except TimedOut: 69 | posix.kill (p.pid, signal.SIGKILL) 70 | raise 71 | 72 | if stderr: 73 | print(stderr.decode (), file=sys.stderr) 74 | 75 | ppds = [] 76 | lines = stdout.decode ().split ('\n') 77 | for line in lines: 78 | l = shlex.split (line) 79 | if len (l) < 1: 80 | continue 81 | ppds.append (l[0]) 82 | 83 | self.ppds = ppds 84 | return ppds 85 | 86 | def cat (self, name): 87 | try: 88 | return self.files[name] 89 | except KeyError: 90 | signal.alarm (10) 91 | p = subprocess.Popen ([self.exe, "cat", name], 92 | stdout=subprocess.PIPE, 93 | stderr=subprocess.PIPE) 94 | try: 95 | (stdout, stderr) = p.communicate () 96 | signal.alarm (0) 97 | except TimedOut: 98 | posix.kill (p.pid, signal.SIGKILL) 99 | raise 100 | 101 | if stderr: 102 | print(stderr.decode (), file=sys.stderr) 103 | 104 | self.files[name] = stdout.decode () 105 | return self.files[name] 106 | 107 | opts, args = getopt (sys.argv[1:], "m:") 108 | if len (args) != 1: 109 | print ("Syntax: test-cups-driver [-m REGEXP] DRIVER") 110 | sys.exit (1) 111 | 112 | match = None 113 | for opt, arg in opts: 114 | if opt == '-m': 115 | match = arg 116 | break 117 | 118 | bad = [] 119 | ids = set() 120 | d = Driver (args[0]) 121 | list = d.list () 122 | 123 | if match: 124 | exp = re.compile (match) 125 | list = [x for x in list if exp.match (x)] 126 | 127 | n = len (list) 128 | i = 0 129 | for name in list: 130 | i += 1 131 | try: 132 | ppd = d.cat (name) 133 | (fd, fname) = tempfile.mkstemp () 134 | f = os.fdopen (fd, "w") 135 | f.write (ppd) 136 | del f 137 | try: 138 | PPD = cups.PPD (fname) 139 | except: 140 | os.unlink (fname) 141 | raise 142 | os.unlink (fname) 143 | 144 | (pkgs, exes) = missingPackagesAndExecutables (PPD) 145 | if pkgs or exes: 146 | raise MissingExecutables 147 | 148 | attr = PPD.findAttr ('1284DeviceID') 149 | if attr: 150 | pieces = attr.value.split (';') 151 | mfg = mdl = None 152 | for piece in pieces: 153 | s = piece.split (':', 1) 154 | if len (s) < 2: 155 | continue 156 | key, value = s 157 | key = key.upper () 158 | if key in ["MFG", "MANUFACTURER"]: 159 | mfg = value 160 | elif key in ["MDL", "MODEL"]: 161 | mdl = value 162 | if mfg and mdl: 163 | id = "MFG:%s;MDL:%s;" % (mfg, mdl) 164 | ids.add (id) 165 | sys.stderr.write ("%3d%%\r" % (100 * i / n)) 166 | sys.stderr.flush () 167 | except KeyboardInterrupt: 168 | print ("Keyboard interrupt\n") 169 | break 170 | except TimedOut as e: 171 | bad.append ((name, e)) 172 | print ("Timed out fetching %s" % name) 173 | except Exception as e: 174 | bad.append ((name, e)) 175 | print ("Exception fetching %s: %s" % (name, e)) 176 | 177 | sys.stdout.flush () 178 | 179 | if len (bad) > 0: 180 | print ("Bad PPDs:") 181 | for each in bad: 182 | print (" %s (%s)" % each) 183 | print 184 | 185 | if len (ids) > 0: 186 | print ("IEEE 1284 Device IDs:") 187 | for each in ids: 188 | print (" %s" % each) 189 | print 190 | -------------------------------------------------------------------------------- /test_PhysicalDevice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2015 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import pytest 22 | try: 23 | import cups 24 | from PhysicalDevice import PhysicalDevice 25 | from cupshelpers import cupshelpers 26 | except ImportError: 27 | cups = None 28 | 29 | @pytest.mark.skipif(cups is None, reason="cups module not available") 30 | def test_ordering(): 31 | # See https://bugzilla.redhat.com/show_bug.cgi?id=1154686 32 | device = cupshelpers.Device("dnssd://Abc%20Def%20%5BABCDEF%5D._ipp._tcp.local/", 33 | **{'device-class': "network", 34 | 'device-make-and-model': "Abc Def", 35 | 'device-id': "MFG:Abc;MDL:Def;"}) 36 | phys = PhysicalDevice (device) 37 | 38 | device = cupshelpers.Device("hp:/net/Abc_Def?hostname=ABCDEF", 39 | **{'device-class': "network", 40 | 'device-make-and-model': "Abc Def", 41 | 'device-id': "MFG:Abc;MDL:Def;"}) 42 | phys.add_device (device) 43 | devices = phys.get_devices () 44 | assert devices[0].uri.startswith ("hp:") 45 | 46 | device = cupshelpers.Device("usb://Abc/Def", 47 | **{'device-class': "direct", 48 | 'device-make-and-model': "Abc Def", 49 | 'device-id': "MFG:Abc;MDL:Def;"}) 50 | phys = PhysicalDevice (device) 51 | device = cupshelpers.Device("hp://Abc/Def", 52 | **{'device-class': "direct", 53 | 'device-make-and-model': "Abc Def", 54 | 'device-id': "MFG:Abc;MDL:Def;"}) 55 | phys.add_device (device) 56 | devices = phys.get_devices () 57 | assert devices[0].uri.startswith ("hp") 58 | 59 | dev1 = cupshelpers.Device("hp:/usb/HP_Color_LaserJet_CP3525?serial=CNCTC8G0QX", 60 | **{'device-id':'MFG:Hewlett-Packard;CMD:PJL,MLC,BIDI-ECP,PJL,PCLXL,PCL,POSTSCRIPT,PDF;MDL:HP Color LaserJet CP3525;CLS:PRINTER;DES:Hewlett-Packard Color LaserJet CP3525;', 61 | 'device-make-and-model':'HP Color LaserJet CP3525', 62 | 'device-class':'direct'}) 63 | phys = PhysicalDevice (dev1) 64 | dev2 = cupshelpers.Device('usb://HP/Color%20LaserJet%20CP3525?serial=CNCTC8G0QX', 65 | **{'device-id':'MFG:Hewlett-Packard;CMD:PJL,MLC,BIDI-ECP,PJL,PCLXL,PCL,POSTSCRIPT,PDF;MDL:HP Color LaserJet CP3525;CLS:PRINTER;DES:Hewlett-Packard Color LaserJet CP3525;', 66 | 'device-make-and-model':'HP Color LaserJet CP3525', 67 | 'device-class':'direct'}) 68 | 69 | # hp device should sort < usb device 70 | assert dev1 < dev2 71 | 72 | phys.add_device (dev2) 73 | devices = phys.get_devices () 74 | assert devices[0] < devices[1] 75 | assert devices[0].uri.startswith ("hp") 76 | -------------------------------------------------------------------------------- /troubleshoot/CheckLocalServerPublishing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import cups 23 | from timedops import TimedOperation 24 | from .base import * 25 | class CheckLocalServerPublishing(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Is local server publishing?") 28 | vbox = self.initial_vbox (_("Server Not Exporting Printers"), 29 | _("Although one or more printers are marked " 30 | "as being shared, this print server is " 31 | "not exporting shared printers to the " 32 | "network.") + '\n\n' + 33 | _("Enable the 'Publish shared printers " 34 | "connected to this system' option in " 35 | "the server settings using the printing " 36 | "administration tool.") + ' ' + 37 | _(TEXT_start_print_admin_tool)) 38 | troubleshooter.new_page (vbox, self) 39 | 40 | def display (self): 41 | self.answers = {} 42 | cups.setServer ('') 43 | parent = self.troubleshooter.get_window () 44 | try: 45 | c = self.timedop (cups.Connection, parent=parent).run () 46 | printers = self.timedop (c.getPrinters, parent=parent).run () 47 | if len (printers) == 0: 48 | return False 49 | 50 | for name, printer in printers.items (): 51 | if printer.get ('printer-is-shared', False): 52 | break 53 | 54 | attr = self.timedop (c.getPrinterAttributes, 55 | args=(name,), 56 | parent=parent).run () 57 | except RuntimeError: 58 | return False 59 | except cups.IPPError: 60 | return False 61 | 62 | if not printer.get ('printer-is-shared', False): 63 | return False 64 | 65 | if attr.get ('server-is-sharing-printers', True): 66 | # server-is-sharing-printers is in CUPS 1.4 67 | return False 68 | 69 | return True 70 | 71 | def collect_answer (self): 72 | if self.displayed: 73 | return { 'local_server_exporting_printers': False } 74 | 75 | return {} 76 | 77 | def cancel_operation (self): 78 | self.op.cancel () 79 | 80 | def timedop (self, *args, **kwargs): 81 | self.op = TimedOperation (*args, **kwargs) 82 | return self.op 83 | -------------------------------------------------------------------------------- /troubleshoot/CheckPrinterSanity.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2012, 2014 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | from gi.repository import Gtk 24 | 25 | import cups 26 | import os 27 | import smburi 28 | import subprocess 29 | from timedops import TimedOperation, TimedSubprocess 30 | import urllib.parse 31 | from .base import * 32 | class CheckPrinterSanity(Question): 33 | def __init__ (self, troubleshooter): 34 | Question.__init__ (self, troubleshooter, "Check printer sanity") 35 | troubleshooter.new_page (Gtk.Label (), self) 36 | self.troubleshooter = troubleshooter 37 | 38 | def display (self): 39 | # Collect information useful for the various checks. 40 | 41 | self.answers = {} 42 | 43 | answers = self.troubleshooter.answers 44 | if not answers['cups_queue_listed']: 45 | return False 46 | 47 | name = answers['cups_queue'] 48 | 49 | parent = self.troubleshooter.get_window () 50 | 51 | # Find out if this is a printer or a class. 52 | try: 53 | cups.setServer ('') 54 | c = TimedOperation (cups.Connection, parent=parent).run () 55 | printers = TimedOperation (c.getPrinters, parent=parent).run () 56 | if name in printers: 57 | self.answers['is_cups_class'] = False 58 | queue = printers[name] 59 | self.answers['cups_printer_dict'] = queue 60 | else: 61 | self.answers['is_cups_class'] = True 62 | classes = TimedOperation (c.getClasses, parent=parent).run () 63 | queue = classes[name] 64 | self.answers['cups_class_dict'] = queue 65 | 66 | attrs = TimedOperation (c.getPrinterAttributes, (name,), 67 | parent=parent).run () 68 | self.answers['local_cups_queue_attributes'] = attrs 69 | except: 70 | pass 71 | 72 | if 'cups_printer_dict' in self.answers: 73 | cups_printer_dict = self.answers['cups_printer_dict'] 74 | uri = cups_printer_dict['device-uri'] 75 | (scheme, rest) = urllib.parse.splittype (uri) 76 | self.answers['cups_device_uri_scheme'] = scheme 77 | if scheme in ["ipp", "http", "https"]: 78 | (hostport, rest) = urllib.parse.splithost (rest) 79 | (host, port) = urllib.parse.splitnport (hostport, defport=631) 80 | self.answers['remote_server_name'] = host 81 | self.answers['remote_server_port'] = port 82 | elif scheme == "smb": 83 | u = smburi.SMBURI (uri) 84 | (group, host, share, user, password) = u.separate () 85 | new_environ = os.environ.copy() 86 | new_environ['LC_ALL'] = "C" 87 | if group: 88 | args = ["nmblookup", "-W", group, host] 89 | else: 90 | args = ["nmblookup", host] 91 | try: 92 | p = TimedSubprocess (parent=parent, 93 | timeout=5000, 94 | args=args, 95 | env=new_environ, 96 | close_fds=True, 97 | stdin=subprocess.DEVNULL, 98 | stdout=subprocess.PIPE, 99 | stderr=subprocess.PIPE) 100 | result = p.run () 101 | self.answers['nmblookup_output'] = result 102 | for line in result[0]: 103 | if line.startswith ("querying"): 104 | continue 105 | spc = line.find (' ') 106 | if (spc != -1 and 107 | not line[spc:].startswith (" failed ")): 108 | # Remember the IP address. 109 | self.answers['remote_server_name'] = line[:spc] 110 | break 111 | except OSError: 112 | # Problem executing command. 113 | pass 114 | elif scheme == "hp": 115 | new_environ = os.environ.copy() 116 | new_environ['LC_ALL'] = "C" 117 | new_environ['DISPLAY'] = "" 118 | try: 119 | p = TimedSubprocess (parent=parent, 120 | timeout=3000, 121 | args=["hp-info", "-d" + uri], 122 | close_fds=True, 123 | env=new_environ, 124 | stdin=subprocess.DEVNULL, 125 | stdout=subprocess.PIPE, 126 | stderr=subprocess.PIPE) 127 | self.answers['hplip_output'] = p.run () 128 | except OSError: 129 | # Problem executing command. 130 | pass 131 | 132 | r = cups_printer_dict['printer-type'] & cups.CUPS_PRINTER_REMOTE 133 | self.answers['cups_printer_remote'] = (r != 0) 134 | return False 135 | 136 | def collect_answer (self): 137 | return self.answers 138 | -------------------------------------------------------------------------------- /troubleshoot/CheckSELinux.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2010, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2010 Jiri Popelka 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import subprocess 25 | from .base import * 26 | import os 27 | import shlex 28 | from timedops import TimedSubprocess 29 | 30 | class CheckSELinux(Question): 31 | def __init__ (self, troubleshooter): 32 | Question.__init__ (self, troubleshooter, "Check SELinux contexts") 33 | troubleshooter.new_page (Gtk.Label (), self) 34 | 35 | def display (self): 36 | self.answers = {} 37 | #answers = self.troubleshooter.answers 38 | 39 | RESTORECON = "/sbin/restorecon" 40 | if not os.access (RESTORECON, os.X_OK): 41 | return False 42 | 43 | try: 44 | import selinux 45 | except ImportError: 46 | return False 47 | if not selinux.is_selinux_enabled(): 48 | return False 49 | 50 | paths = ["/etc/cups/", "/usr/lib/cups/", "/usr/share/cups/"] 51 | parent = self.troubleshooter.get_window () 52 | contexts = {} 53 | new_environ = os.environ.copy() 54 | new_environ['LC_ALL'] = "C" 55 | restorecon_args = [RESTORECON, "-nvR"].extend(paths) 56 | try: 57 | # Run restorecon -nvR 58 | self.op = TimedSubprocess (parent=parent, 59 | args=restorecon_args, 60 | close_fds=True, 61 | env=new_environ, 62 | stdin=subprocess.DEVNULL, 63 | stdout=subprocess.PIPE, 64 | stderr=subprocess.DEVNULL) 65 | (restorecon_stdout, restorecon_stderr, result) = self.op.run () 66 | except: 67 | # Problem executing command. 68 | return False 69 | for line in restorecon_stdout: 70 | l = shlex.split (line) 71 | if (len (l) < 1): 72 | continue 73 | contexts[l[2]] = l[4] 74 | self.answers['selinux_contexts'] = contexts 75 | return False 76 | 77 | def collect_answer (self): 78 | return self.answers 79 | 80 | def cancel_operation (self): 81 | self.op.cancel () 82 | -------------------------------------------------------------------------------- /troubleshoot/CheckUSBPermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2014 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | import glob 24 | import os 25 | import subprocess 26 | from timedops import TimedSubprocess 27 | import urllib.parse 28 | from .base import * 29 | from gi.repository import Gtk 30 | 31 | class CheckUSBPermissions(Question): 32 | def __init__ (self, troubleshooter): 33 | Question.__init__ (self, troubleshooter, "Check USB permissions") 34 | troubleshooter.new_page (Gtk.Label (), self) 35 | 36 | def display (self): 37 | self.answers = {} 38 | answers = self.troubleshooter.answers 39 | if answers['cups_queue_listed']: 40 | if answers['is_cups_class']: 41 | return False 42 | 43 | cups_printer_dict = answers['cups_printer_dict'] 44 | device_uri = cups_printer_dict['device-uri'] 45 | elif answers.get ('cups_device_listed', False): 46 | device_uri = answers['cups_device_uri'] 47 | else: 48 | return False 49 | 50 | (scheme, rest) = urllib.parse.splittype (device_uri) 51 | if scheme not in ['hp', 'hpfax', 'usb', 'hal']: 52 | return False 53 | 54 | LSUSB = "/sbin/lsusb" 55 | if not os.access (LSUSB, os.X_OK): 56 | return False 57 | 58 | GETFACL = "/usr/bin/getfacl" 59 | if not os.access (GETFACL, os.X_OK): 60 | return False 61 | 62 | new_environ = os.environ.copy() 63 | new_environ['LC_ALL'] = "C" 64 | 65 | # Run lsusb 66 | parent = self.troubleshooter.get_window () 67 | try: 68 | self.op = TimedSubprocess (parent=parent, 69 | args=[LSUSB, "-v"], 70 | close_fds=True, 71 | env=new_environ, 72 | stdin=subprocess.DEVNULL, 73 | stdout=subprocess.PIPE, 74 | stderr=subprocess.PIPE) 75 | (lsusb_stdout, lsusb_stderr, result) = self.op.run () 76 | except: 77 | # Problem executing command. 78 | return False 79 | 80 | # Now parse it. 81 | dev_by_id = {} 82 | this_dev = None 83 | for line in lsusb_stdout: 84 | if (this_dev is not None and 85 | ((line.find ("bInterfaceClass") != -1 and 86 | line.find ("7 Printer") != -1) or 87 | (line.find ("bInterfaceSubClass") != -1 and 88 | line.find ("1 Printer") != -1))): 89 | mfr = dev_by_id.get (this_mfr_id, {}) 90 | mdl = mfr.get (this_mdl_id, []) 91 | mdl.append (this_dev) 92 | mfr[this_mdl_id] = mdl 93 | dev_by_id[this_mfr_id] = mfr 94 | this_dev = None 95 | continue 96 | 97 | separators = [ ('Bus ', 3), 98 | (' Device ', 3), 99 | (': ID ', 4), 100 | (':', 4), 101 | (' ', -1)] 102 | fields = [] 103 | i = 0 104 | p = line 105 | while i < len (separators): 106 | (sep, length) = separators[i] 107 | if not p.startswith (sep): 108 | break 109 | start = len (sep) 110 | if length == -1: 111 | end = len (p) 112 | fields.append (p[start:]) 113 | else: 114 | end = start + length 115 | fields.append (p[start:end]) 116 | 117 | p = p[end:] 118 | i += 1 119 | 120 | if i < len (separators): 121 | continue 122 | 123 | if not scheme.startswith ('hp') and fields[2] != '03f0': 124 | # Skip non-HP printers if we know we're using HPLIP. 125 | continue 126 | 127 | this_dev = { 'bus': fields[0], 128 | 'dev': fields[1], 129 | 'name': fields[4], 130 | 'full': line } 131 | this_mfr_id = fields[2] 132 | this_mdl_id = fields[3] 133 | 134 | infos = {} 135 | paths = [] 136 | if not scheme.startswith ('hp'): 137 | paths.extend (glob.glob ("/dev/usb/lp?")) 138 | for mfr_id, mdls in dev_by_id.items (): 139 | for mdl_id, devs in mdls.items (): 140 | for dev in devs: 141 | path = "/dev/bus/usb/%s/%s" % (dev['bus'], dev['dev']) 142 | paths.append (path) 143 | infos[path] = dev['full'] 144 | 145 | perms = [] 146 | for path in paths: 147 | try: 148 | self.op = TimedSubprocess (parent=parent, 149 | args=[GETFACL, path], 150 | close_fds=True, 151 | env=new_environ, 152 | stdin=subprocess.DEVNULL, 153 | stdout=subprocess.PIPE, 154 | stderr=subprocess.PIPE) 155 | (getfacl_stdout, getfacl_stderr, result) = self.op.run () 156 | output = [x for x in getfacl_stdout if len (x) > 0] 157 | except: 158 | # Problem executing command. 159 | output = [] 160 | 161 | info = infos.get (path, path) 162 | perms.append ((info, output)) 163 | 164 | self.answers['getfacl_output'] = perms 165 | 166 | # Don't actually display anything, just collect information. 167 | return False 168 | 169 | def collect_answer (self): 170 | return self.answers 171 | 172 | def cancel_operation (self): 173 | self.op.cancel () 174 | -------------------------------------------------------------------------------- /troubleshoot/ChoosePrinter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2012 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | class NoPrinter: 25 | pass 26 | 27 | NotListed = NoPrinter() 28 | 29 | import cups 30 | from gi.repository import GObject 31 | from timedops import TimedOperation 32 | from .base import * 33 | class ChoosePrinter(Question): 34 | def __init__ (self, troubleshooter): 35 | # First question: which printer? (page 1) 36 | Question.__init__ (self, troubleshooter, "Choose printer") 37 | page1 = self.initial_vbox (_("Choose Printer"), 38 | _("Please select the printer you are " 39 | "trying to use from the list below. " 40 | "If it does not appear in the list, " 41 | "select 'Not listed'.")) 42 | tv = Gtk.TreeView () 43 | name = Gtk.TreeViewColumn (_("Name"), 44 | Gtk.CellRendererText (), text=0) 45 | location = Gtk.TreeViewColumn (_("Location"), 46 | Gtk.CellRendererText (), text=1) 47 | info = Gtk.TreeViewColumn (_("Information"), 48 | Gtk.CellRendererText (), text=2) 49 | name.set_property ("resizable", True) 50 | location.set_property ("resizable", True) 51 | info.set_property ("resizable", True) 52 | tv.append_column (name) 53 | tv.append_column (location) 54 | tv.append_column (info) 55 | tv.set_rules_hint (True) 56 | sw = Gtk.ScrolledWindow () 57 | sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) 58 | sw.set_shadow_type (Gtk.ShadowType.IN) 59 | sw.add (tv) 60 | page1.pack_start (sw, True, True, 0) 61 | self.treeview = tv 62 | troubleshooter.new_page (page1, self) 63 | 64 | def display (self): 65 | model = Gtk.ListStore (str, 66 | str, 67 | str, 68 | GObject.TYPE_PYOBJECT) 69 | self.treeview.set_model (model) 70 | iter = model.append (None) 71 | model.set (iter, 0, _("Not listed"), 1, '', 2, '', 3, NotListed) 72 | 73 | parent = self.troubleshooter.get_window () 74 | try: 75 | cups.setServer ('') 76 | c = self.timedop (cups.Connection, parent=parent).run () 77 | dests = self.timedop (c.getDests, parent=parent).run () 78 | printers = None 79 | dests_list = [] 80 | for (name, instance), dest in dests.items (): 81 | if name is None: 82 | continue 83 | 84 | if instance is not None: 85 | queue = "%s/%s" % (name, instance) 86 | else: 87 | queue = name 88 | 89 | if printers is None: 90 | printers = self.timedop (c.getPrinters, 91 | parent=parent).run () 92 | 93 | if name not in printers: 94 | info = _("Unknown") 95 | location = _("Unknown") 96 | else: 97 | printer = printers[name] 98 | info = printer.get('printer-info', _("Unknown")) 99 | location = printer.get('printer-location', _("Unknown")) 100 | 101 | dests_list.append ((queue, location, info, dest)) 102 | 103 | dests_list.sort (key=lambda x: x[0]) 104 | for queue, location, info, dest in dests_list: 105 | iter = model.append (None) 106 | model.set (iter, 0, queue, 1, location, 2, info, 3, dest) 107 | 108 | except cups.HTTPError: 109 | pass 110 | except cups.IPPError: 111 | pass 112 | except RuntimeError: 113 | pass 114 | 115 | return True 116 | 117 | def connect_signals (self, handler): 118 | self.signal_id = self.treeview.connect ("cursor-changed", handler) 119 | 120 | def disconnect_signals (self): 121 | self.treeview.disconnect (self.signal_id) 122 | 123 | def can_click_forward (self): 124 | model, iter = self.treeview.get_selection ().get_selected () 125 | if iter is None: 126 | return False 127 | return True 128 | 129 | def collect_answer (self): 130 | model, iter = self.treeview.get_selection ().get_selected () 131 | dest = model.get_value (iter, 3) 132 | if dest == NotListed: 133 | class enum_dests: 134 | def __init__ (self, model): 135 | self.dests = [] 136 | model.foreach (self.each, None) 137 | 138 | def each (self, model, path, iter, user_data): 139 | dest = model.get_value (iter, 3) 140 | if dest != NotListed: 141 | self.dests.append ((dest.name, 142 | dest.instance)) 143 | 144 | return { 'cups_queue_listed': False, 145 | 'cups_dests_available': enum_dests (model).dests } 146 | else: 147 | return { 'cups_queue_listed': True, 148 | 'cups_dest': dest, 149 | 'cups_queue': dest.name, 150 | 'cups_instance': dest.instance } 151 | 152 | def cancel_operation (self): 153 | self.op.cancel () 154 | 155 | def timedop (self, *args, **kwargs): 156 | self.op = TimedOperation (*args, **kwargs) 157 | return self.op 158 | -------------------------------------------------------------------------------- /troubleshoot/ErrorLogParse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2012, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | from functools import reduce 26 | class ErrorLogParse(Question): 27 | 28 | ## This could be a LOT smarter. 29 | 30 | def __init__ (self, troubleshooter): 31 | Question.__init__ (self, troubleshooter, "Error log parse") 32 | page = self.initial_vbox (_("Error log messages"), 33 | _("There are messages in the error log.")) 34 | sw = Gtk.ScrolledWindow () 35 | textview = Gtk.TextView () 36 | textview.set_editable (False) 37 | sw.add (textview) 38 | page.pack_start (sw, True, True, 0) 39 | self.buffer = textview.get_buffer () 40 | troubleshooter.new_page (page, self) 41 | 42 | def display (self): 43 | answers = self.troubleshooter.answers 44 | try: 45 | journal = answers.get ('journal') 46 | error_log = answers.get ('error_log') 47 | except KeyError: 48 | return False 49 | 50 | display = False 51 | if error_log: 52 | for line in error_log: 53 | if line[0] == 'E': 54 | display = error_log 55 | break 56 | 57 | if journal and not display: 58 | for line in journal: 59 | if line[0] == 'E': 60 | display = journal 61 | break 62 | 63 | if display: 64 | self.buffer.set_text (reduce (lambda x, y: x + '\n' + y, 65 | display)) 66 | 67 | return display != False 68 | -------------------------------------------------------------------------------- /troubleshoot/LocalOrRemote.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from .base import * 23 | class LocalOrRemote(Multichoice): 24 | def __init__ (self, troubleshooter): 25 | Multichoice.__init__ (self, troubleshooter, "printer_is_remote", 26 | _("Printer Location"), 27 | _("Is the printer connected to this computer " 28 | "or available on the network?"), 29 | [(_("Locally connected printer"), False), 30 | (_("Network printer"), True)], 31 | "Local or remote?") 32 | 33 | def display (self): 34 | return not self.troubleshooter.answers['cups_queue_listed'] 35 | -------------------------------------------------------------------------------- /troubleshoot/Locale.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2012 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2012 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import locale 23 | 24 | from gi.repository import Gtk 25 | 26 | from .base import * 27 | 28 | class Locale(Question): 29 | def __init__ (self, troubleshooter): 30 | Question.__init__ (self, troubleshooter, "Locale issues") 31 | page = self.initial_vbox (_("Incorrect Page Size"), 32 | _("The page size for the print job was " 33 | "not the printer's default page size. " 34 | "If this is not intentional it may cause " 35 | "alignment problems.")) 36 | grid = Gtk.Grid() 37 | grid.set_row_spacing (6) 38 | grid.set_column_spacing (6) 39 | page.pack_start (grid, False, False, 0) 40 | self.printer_page_size = Gtk.Label () 41 | self.printer_page_size.set_alignment (0, 0) 42 | self.job_page_size = Gtk.Label () 43 | self.job_page_size.set_alignment (0, 0) 44 | label = Gtk.Label(label=_("Print job page size:")) 45 | label.set_alignment (0, 0) 46 | grid.attach (label, 0, 0, 1, 1) 47 | grid.attach (self.job_page_size, 1, 0, 1, 1) 48 | label = Gtk.Label(label=_("Printer page size:")) 49 | label.set_alignment (0, 0) 50 | grid.attach (label, 0, 1, 1, 1) 51 | grid.attach (self.printer_page_size, 1, 1, 1, 1) 52 | troubleshooter.new_page (page, self) 53 | 54 | def display (self): 55 | self.answers = {} 56 | (messages, encoding) = locale.getlocale (locale.LC_MESSAGES) 57 | (ctype, encoding) = locale.getlocale (locale.LC_CTYPE) 58 | self.answers['user_locale_messages'] = messages 59 | self.answers['user_locale_ctype'] = ctype 60 | 61 | try: 62 | system_lang = None 63 | conf = None 64 | for conffile in ["/etc/locale.conf", "/etc/sysconfig/i18n"]: 65 | try: 66 | conf = open (conffile).readlines () 67 | except IOError: 68 | continue 69 | 70 | if conf is not None: 71 | for line in conf: 72 | if line.startswith("LC_PAPER="): 73 | system_lang = line[9:].strip ('\n"') 74 | elif system_lang is None and line.startswith ("LANG="): 75 | system_lang = line[5:].strip ('\n"') 76 | 77 | if system_lang is not None: 78 | dot = system_lang.find ('.') 79 | if dot != -1: 80 | system_lang = system_lang[:dot] 81 | except: 82 | system_lang = None 83 | 84 | self.answers['system_locale_lang'] = system_lang 85 | 86 | printer_page_size = None 87 | try: 88 | ppd_defs = self.troubleshooter.answers['cups_printer_ppd_defaults'] 89 | for group, options in ppd_defs.items (): 90 | if "PageSize" in options: 91 | printer_page_size = options["PageSize"] 92 | break 93 | 94 | except KeyError: 95 | try: 96 | attrs = self.troubleshooter.answers['remote_cups_queue_attributes'] 97 | printer_page_size = attrs["media-default"] 98 | except KeyError: 99 | pass 100 | 101 | try: 102 | job_status = self.troubleshooter.answers["test_page_job_status"] 103 | except KeyError: 104 | job_status = [] 105 | 106 | self.answers['printer_page_size'] = printer_page_size 107 | if printer_page_size is not None: 108 | job_page_size = None 109 | for (test, jobid, printer, doc, status, attrs) in job_status: 110 | if test: 111 | if "PageSize" in attrs: 112 | job_page_size = attrs["PageSize"] 113 | self.answers['job_page_size'] = job_page_size 114 | if job_page_size != printer_page_size: 115 | self.printer_page_size.set_text (printer_page_size) 116 | self.job_page_size.set_text (job_page_size) 117 | return True 118 | 119 | return False 120 | 121 | def collect_answer (self): 122 | return self.answers 123 | 124 | -------------------------------------------------------------------------------- /troubleshoot/NetworkCUPSPrinterShared.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import cups 23 | from timedops import TimedOperation 24 | from .base import * 25 | class NetworkCUPSPrinterShared(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Queue not shared?") 28 | page = self.initial_vbox (_("Queue Not Shared"), 29 | _("The CUPS printer on the server is not " 30 | "shared.")) 31 | troubleshooter.new_page (page, self) 32 | 33 | def display (self): 34 | self.answers = {} 35 | answers = self.troubleshooter.answers 36 | if ('remote_cups_queue_listed' in answers and 37 | answers['remote_cups_queue_listed'] == False): 38 | return False 39 | 40 | parent = self.troubleshooter.get_window () 41 | if 'remote_cups_queue_attributes' not in answers: 42 | if not ('remote_server_try_connect' in answers and 43 | 'remote_cups_queue' in answers): 44 | return False 45 | 46 | try: 47 | host = answers['remote_server_try_connect'] 48 | self.op = TimedOperation (cups.Connection, 49 | kwargs={"host": host}, 50 | parent=parent) 51 | c = self.op.run () 52 | self.op = TimedOperation (c.getPrinterAttributes, 53 | args=(answers['remote_cups_queue'],), 54 | parent=parent) 55 | attr = self.op.run () 56 | except RuntimeError: 57 | return False 58 | except cups.IPPError: 59 | return False 60 | 61 | self.answers['remote_cups_queue_attributes'] = attr 62 | else: 63 | attr = answers['remote_cups_queue_attributes'] 64 | 65 | if 'printer-is-shared' in attr: 66 | # CUPS >= 1.2 67 | if not attr['printer-is-shared']: 68 | return True 69 | 70 | return False 71 | 72 | def can_click_forward (self): 73 | return False 74 | 75 | def collect_answer (self): 76 | return self.answers 77 | 78 | def cancel_operation (self): 79 | self.op.cancel () 80 | -------------------------------------------------------------------------------- /troubleshoot/PrinterStateReasons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2011 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | from gi.repository import Gtk 24 | 25 | import cups 26 | import ppdcache 27 | import statereason 28 | from timedops import TimedOperation 29 | from .base import * 30 | from functools import reduce 31 | class PrinterStateReasons(Question): 32 | def __init__ (self, troubleshooter): 33 | Question.__init__ (self, troubleshooter, "Printer state reasons") 34 | page = self.initial_vbox (_("Status Messages"), 35 | _("There are status messages associated with " 36 | "this queue.")) 37 | self.label = Gtk.Label () 38 | self.label.set_alignment (0, 0) 39 | self.label.set_line_wrap (True) 40 | page.pack_start (self.label, False, False, 0) 41 | 42 | troubleshooter.new_page (page, self) 43 | 44 | def display (self): 45 | troubleshooter = self.troubleshooter 46 | try: 47 | queue = troubleshooter.answers['cups_queue'] 48 | except KeyError: 49 | return False 50 | 51 | parent = self.troubleshooter.get_window () 52 | cups.setServer ('') 53 | self.op = TimedOperation (cups.Connection, parent=parent) 54 | c = self.op.run () 55 | self.op = TimedOperation (c.getPrinterAttributes, 56 | args=(queue,), 57 | parent=parent) 58 | dict = self.op.run () 59 | 60 | the_ppdcache = ppdcache.PPDCache () 61 | 62 | text = '' 63 | state_message = dict['printer-state-message'] 64 | if state_message: 65 | text += _("The printer's state message is: '%s'.") % state_message 66 | text += '\n\n' 67 | 68 | state_reasons_list = dict['printer-state-reasons'] 69 | if type (state_reasons_list) == str: 70 | state_reasons_list = [state_reasons_list] 71 | 72 | self.state_message = state_message 73 | self.state_reasons = state_reasons_list 74 | 75 | human_readable_errors = [] 76 | human_readable_warnings = [] 77 | for reason in state_reasons_list: 78 | if reason == "none": 79 | continue 80 | 81 | r = statereason.StateReason (queue, reason, the_ppdcache) 82 | (title, description) = r.get_description () 83 | level = r.get_level () 84 | if level == statereason.StateReason.ERROR: 85 | human_readable_errors.append (description) 86 | elif level == statereason.StateReason.WARNING: 87 | human_readable_warnings.append (description) 88 | 89 | if human_readable_errors: 90 | text += _("Errors are listed below:") + '\n' 91 | text += reduce (lambda x, y: x + "\n" + y, human_readable_errors) 92 | text += '\n\n' 93 | 94 | if human_readable_warnings: 95 | text += _("Warnings are listed below:") + '\n' 96 | text += reduce (lambda x, y: x + "\n" + y, human_readable_warnings) 97 | 98 | self.label.set_text (text) 99 | if (state_message == '' and 100 | len (human_readable_errors) == 0 and 101 | len (human_readable_warnings) == 0): 102 | return False 103 | 104 | # If this screen has been show before, don't show it again if 105 | # nothing changed. 106 | if 'printer-state-message' in troubleshooter.answers: 107 | if (troubleshooter.answers['printer-state-message'] == 108 | self.state_message and 109 | troubleshooter.answers['printer-state-reasons'] == 110 | self.state_reasons): 111 | return False 112 | 113 | return True 114 | 115 | def collect_answer (self): 116 | if not self.displayed: 117 | return {} 118 | 119 | return { 'printer-state-message': self.state_message, 120 | 'printer-state-reasons': self.state_reasons } 121 | 122 | def cancel_operation (self): 123 | self.op.cancel () 124 | -------------------------------------------------------------------------------- /troubleshoot/QueueNotEnabled.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import cups 25 | from .base import * 26 | class QueueNotEnabled(Question): 27 | def __init__ (self, troubleshooter): 28 | Question.__init__ (self, troubleshooter, "Queue not enabled?") 29 | self.label = Gtk.Label () 30 | solution = Gtk.VBox () 31 | self.label.set_line_wrap (True) 32 | self.label.set_alignment (0, 0) 33 | solution.pack_start (self.label, False, False, 0) 34 | solution.set_border_width (12) 35 | troubleshooter.new_page (solution, self) 36 | 37 | def display (self): 38 | answers = self.troubleshooter.answers 39 | if not answers['cups_queue_listed']: 40 | return False 41 | 42 | if answers['is_cups_class']: 43 | queue = answers['cups_class_dict'] 44 | else: 45 | queue = answers['cups_printer_dict'] 46 | 47 | enabled = queue['printer-state'] != cups.IPP_PRINTER_STOPPED 48 | if enabled: 49 | return False 50 | 51 | if answers['cups_printer_remote']: 52 | attrs = answers['remote_cups_queue_attributes'] 53 | reason = attrs['printer-state-message'] 54 | else: 55 | reason = queue['printer-state-message'] 56 | 57 | if reason: 58 | reason = _("The reason given is: '%s'.") % reason 59 | else: 60 | reason = _("This may be due to the printer being disconnected or " 61 | "switched off.") 62 | 63 | text = ('' + 64 | _("Queue Not Enabled") + '\n\n' + 65 | _("The queue '%s' is not enabled.") % 66 | answers['cups_queue']) 67 | 68 | if reason: 69 | text += ' ' + reason 70 | 71 | if not answers['cups_printer_remote']: 72 | text += '\n\n' 73 | text += _("To enable it, select the 'Enabled' checkbox in the " 74 | "'Policies' tab for the printer in the printer " 75 | "administration tool.") 76 | text += ' ' + _(TEXT_start_print_admin_tool) 77 | 78 | self.label.set_markup (text) 79 | return True 80 | 81 | def can_click_forward (self): 82 | return False 83 | -------------------------------------------------------------------------------- /troubleshoot/QueueRejectingJobs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import cups 25 | from .base import * 26 | class QueueRejectingJobs(Question): 27 | def __init__ (self, troubleshooter): 28 | Question.__init__ (self, troubleshooter, "Queue rejecting jobs?") 29 | solution = Gtk.VBox () 30 | solution.set_border_width (12) 31 | solution.set_spacing (12) 32 | label = Gtk.Label(label='' + 33 | _("Queue Rejecting Jobs") + '') 34 | label.set_alignment (0, 0) 35 | label.set_use_markup (True) 36 | solution.pack_start (label, False, False, 0) 37 | self.label = Gtk.Label () 38 | self.label.set_alignment (0, 0) 39 | self.label.set_line_wrap (True) 40 | solution.pack_start (self.label, False, False, 0) 41 | solution.set_border_width (12) 42 | 43 | troubleshooter.new_page (solution, self) 44 | 45 | def display (self): 46 | answers = self.troubleshooter.answers 47 | if not answers['cups_queue_listed']: 48 | return False 49 | 50 | if answers['is_cups_class']: 51 | queue = answers['cups_class_dict'] 52 | else: 53 | queue = answers['cups_printer_dict'] 54 | 55 | rejecting = queue['printer-type'] & cups.CUPS_PRINTER_REJECTING 56 | if not rejecting: 57 | return False 58 | 59 | if answers['cups_printer_remote']: 60 | attrs = answers['remote_cups_queue_attributes'] 61 | reason = attrs['printer-state-message'] 62 | else: 63 | reason = queue['printer-state-message'] 64 | 65 | text = (_("The queue '%s' is rejecting jobs.") % answers['cups_queue']) 66 | 67 | if reason: 68 | text += ' ' + _("The reason given is: '%s'.") % reason 69 | 70 | if not answers['cups_printer_remote']: 71 | text += "\n\n" 72 | text += _("To make the queue accept jobs, select the " 73 | "'Accepting Jobs' checkbox in the 'Policies' " 74 | "tab for the printer in the printer administration " 75 | "tool.") + ' ' + _(TEXT_start_print_admin_tool) 76 | 77 | self.label.set_text (text) 78 | return True 79 | 80 | def can_click_forward (self): 81 | return False 82 | -------------------------------------------------------------------------------- /troubleshoot/RemoteAddress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | class RemoteAddress(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Remote address") 28 | page = self.initial_vbox (_("Remote Address"), 29 | _("Please enter as many details as you " 30 | "can about the network address of this " 31 | "printer.")) 32 | grid = Gtk.Grid() 33 | grid.set_row_spacing (6) 34 | grid.set_column_spacing (6) 35 | page.pack_start (grid, False, False, 0) 36 | 37 | label = Gtk.Label(label=_("Server name:")) 38 | label.set_alignment (0, 0) 39 | grid.attach (label, 0, 0, 1, 1) 40 | self.server_name = Gtk.Entry () 41 | self.server_name.set_activates_default (True) 42 | grid.attach (self.server_name, 1, 0, 1, 1) 43 | 44 | label = Gtk.Label(label=_("Server IP address:")) 45 | label.set_alignment (0, 0) 46 | grid.attach (label, 0, 1, 1, 1) 47 | self.server_ipaddr = Gtk.Entry () 48 | self.server_ipaddr.set_activates_default (True) 49 | grid.attach (self.server_ipaddr, 1, 1, 1, 1) 50 | 51 | troubleshooter.new_page (page, self) 52 | 53 | def display (self): 54 | answers = self.troubleshooter.answers 55 | if answers['cups_queue_listed']: 56 | return False 57 | 58 | return answers['printer_is_remote'] 59 | 60 | def collect_answer (self): 61 | if not self.displayed: 62 | return {} 63 | 64 | return { 'remote_server_name': self.server_name.get_text (), 65 | 'remote_server_ip_address': self.server_ipaddr.get_text () } 66 | -------------------------------------------------------------------------------- /troubleshoot/SchedulerNotRunning.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import cups 23 | from timedops import TimedOperation 24 | from .base import * 25 | class SchedulerNotRunning(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Scheduler not running?") 28 | page = self.initial_vbox (_("CUPS Service Stopped"), 29 | _("The CUPS print spooler does not appear " 30 | "to be running. To correct this, choose " 31 | "System->Administration->Services from " 32 | "the main menu and look for the 'cups' " 33 | "service.")) 34 | troubleshooter.new_page (page, self) 35 | 36 | def display (self): 37 | self.answers = {} 38 | if self.troubleshooter.answers.get ('cups_queue_listed', False): 39 | return False 40 | 41 | parent = self.troubleshooter.get_window () 42 | 43 | # Find out if CUPS is running. 44 | failure = False 45 | try: 46 | self.op = TimedOperation (cups.Connection, 47 | parent=parent) 48 | c = self.op.run () 49 | except RuntimeError: 50 | failure = True 51 | 52 | self.answers['cups_connection_failure'] = failure 53 | return failure 54 | 55 | def can_click_forward (self): 56 | return False 57 | 58 | def collect_answer (self): 59 | return self.answers 60 | 61 | def cancel_operation (self): 62 | self.op.cancel () 63 | -------------------------------------------------------------------------------- /troubleshoot/ServerFirewalled.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | class ServerFirewalled(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Server firewalled") 28 | page = self.initial_vbox (_("Check Server Firewall"), 29 | _("It is not possible to connect to the " 30 | "server.")) 31 | self.label = Gtk.Label () 32 | self.label.set_alignment (0, 0) 33 | self.label.set_line_wrap (True) 34 | page.pack_start (self.label, False, False, 0) 35 | troubleshooter.new_page (page, self) 36 | 37 | def display (self): 38 | answers = self.troubleshooter.answers 39 | if not answers['cups_queue_listed']: 40 | return False 41 | 42 | if ('remote_server_connect_ipp' in answers and 43 | answers['remote_server_connect_ipp'] == False): 44 | self.label.set_text (_("Please check to see if a firewall or " 45 | "router configuration is blocking TCP " 46 | "port %d on server '%s'.") 47 | % (answers['remote_server_port'], 48 | answers['remote_server_try_connect'])) 49 | return True 50 | return False 51 | 52 | def can_click_forward (self): 53 | return False 54 | -------------------------------------------------------------------------------- /troubleshoot/Shrug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. 6 | ## Author: Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | class Shrug(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Shrug") 28 | page = self.initial_vbox (_("Sorry!"), 29 | _("There is no obvious solution to this " 30 | "problem. Your answers have been " 31 | "collected together with " 32 | "other useful information. If you " 33 | "would like to report a bug, please " 34 | "include this information.")) 35 | 36 | expander = Gtk.Expander.new(_("Diagnostic Output (Advanced)")) 37 | expander.set_expanded (False) 38 | sw = Gtk.ScrolledWindow () 39 | expander.add (sw) 40 | textview = Gtk.TextView () 41 | textview.set_editable (False) 42 | sw.add (textview) 43 | page.pack_start (expander, True, True, 0) 44 | self.buffer = textview.get_buffer () 45 | 46 | box = Gtk.HButtonBox () 47 | box.set_border_width (0) 48 | box.set_spacing (3) 49 | box.set_layout (Gtk.ButtonBoxStyle.END) 50 | page.pack_start (box, False, False, 0) 51 | 52 | self.save = Gtk.Button.new_from_stock (Gtk.STOCK_SAVE) 53 | box.pack_start (self.save, False, False, 0) 54 | 55 | troubleshooter.new_page (page, self) 56 | 57 | def display (self): 58 | self.buffer.set_text (self.troubleshooter.answers_as_text ()) 59 | return True 60 | 61 | def connect_signals (self, handler): 62 | self.save_sigid = self.save.connect ('clicked', self.on_save_clicked) 63 | 64 | def disconnect_signals (self): 65 | self.save.disconnect (self.save_sigid) 66 | 67 | def on_save_clicked (self, button): 68 | while True: 69 | parent = self.troubleshooter.get_window() 70 | dialog = Gtk.FileChooserDialog (transient_for=parent, 71 | action=Gtk.FileChooserAction.SAVE) 72 | dialog.add_buttons (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 73 | Gtk.STOCK_SAVE, Gtk.ResponseType.OK) 74 | dialog.set_do_overwrite_confirmation (True) 75 | dialog.set_current_name ("troubleshoot.txt") 76 | dialog.set_default_response (Gtk.ResponseType.OK) 77 | dialog.set_local_only (True) 78 | response = dialog.run () 79 | dialog.hide () 80 | if response != Gtk.ResponseType.OK: 81 | return 82 | 83 | try: 84 | f = open (dialog.get_filename (), "w") 85 | f.write (self.buffer.get_text (start=self.buffer.get_start_iter (), 86 | end=self.buffer.get_end_iter (), 87 | include_hidden_chars=False)) 88 | except IOError as e: 89 | err = Gtk.MessageDialog (parent=parent, 90 | modal=True, destroy_with_parent=True, 91 | message_type=Gtk.MessageType.ERROR, 92 | buttons=Gtk.ButtonsType.CLOSE, 93 | text=_("Error saving file")) 94 | err.format_secondary_text (_("There was an error saving " 95 | "the file:") + "\n" + 96 | e.strerror) 97 | err.run () 98 | err.destroy () 99 | continue 100 | 101 | del f 102 | break 103 | -------------------------------------------------------------------------------- /troubleshoot/VerifyPackages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2010, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2010 Jiri Popelka 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import subprocess 25 | from .base import * 26 | import os 27 | from timedops import TimedSubprocess 28 | 29 | class VerifyPackages(Question): 30 | def __init__ (self, troubleshooter): 31 | Question.__init__ (self, troubleshooter, "Verify packages") 32 | troubleshooter.new_page (Gtk.Label (), self) 33 | 34 | def display (self): 35 | self.answers = {} 36 | packages_verification = {} 37 | 38 | package_manager="/bin/rpm" 39 | if not os.access (package_manager, os.X_OK): 40 | return False 41 | 42 | packages = ["cups", 43 | "foomatic", 44 | "gutenprint", 45 | "hpijs", 46 | "hplip", 47 | "system-config-printer"] 48 | parent = self.troubleshooter.get_window () 49 | 50 | new_environ = os.environ.copy() 51 | new_environ['LC_ALL'] = "C" 52 | 53 | for package in packages: 54 | verification_args = [package_manager, "-V", package] 55 | try: 56 | self.op = TimedSubprocess (parent=parent, 57 | args=verification_args, 58 | close_fds=True, 59 | env=new_environ, 60 | stdin=subprocess.DEVNULL, 61 | stdout=subprocess.PIPE, 62 | stderr=subprocess.DEVNULL) 63 | (verif_stdout, verif_stderr, result) = self.op.run () 64 | except: 65 | # Problem executing command. 66 | return False 67 | packages_verification[package] = verif_stdout[:-1] 68 | 69 | self.answers['packages_verification'] = packages_verification 70 | return False 71 | 72 | def collect_answer (self): 73 | return self.answers 74 | 75 | def cancel_operation (self): 76 | self.op.cancel () 77 | -------------------------------------------------------------------------------- /troubleshoot/Welcome.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Author: Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | from timedops import TimedOperation 26 | import authconn 27 | 28 | class AuthConnFactory: 29 | def __init__ (self, parent): 30 | self.parent = parent 31 | 32 | def get_connection (self): 33 | return authconn.Connection (self.parent, lock=True) 34 | 35 | class Welcome(Question): 36 | def __init__ (self, troubleshooter): 37 | Question.__init__ (self, troubleshooter, "Welcome") 38 | welcome = Gtk.HBox () 39 | welcome.set_spacing (12) 40 | welcome.set_border_width (12) 41 | image = Gtk.Image () 42 | image.set_alignment (0, 0) 43 | image.set_from_stock (Gtk.STOCK_PRINT, Gtk.IconSize.DIALOG) 44 | intro = Gtk.Label(label='' + 45 | _("Trouble-shooting Printing") + 46 | '\n\n' + 47 | _("The next few screens will contain some " 48 | "questions about your problem with printing. " 49 | "Based on your answers a solution may be " 50 | "suggested.") + '\n\n' + 51 | _("Click 'Forward' to begin.")) 52 | intro.set_alignment (0, 0) 53 | intro.set_use_markup (True) 54 | intro.set_line_wrap (True) 55 | welcome.pack_start (image, False, False, 0) 56 | welcome.pack_start (intro, True, True, 0) 57 | page = troubleshooter.new_page (welcome, self) 58 | 59 | def collect_answer (self): 60 | parent = self.troubleshooter.get_window () 61 | # Store the authentication dialog instance in the answers. This 62 | # allows the password to be cached. 63 | factory = AuthConnFactory (parent) 64 | self.op = TimedOperation (factory.get_connection, parent=parent) 65 | return {'_authenticated_connection_factory': factory, 66 | '_authenticated_connection': self.op.run () } 67 | 68 | def cancel_operation (self): 69 | self.op.cancel () 70 | -------------------------------------------------------------------------------- /troubleshoot/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2010, 2012 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | from gi.repository import Gtk 24 | N_ = lambda x: x 25 | from debug import * 26 | 27 | import config 28 | import gettext 29 | gettext.install(domain=config.PACKAGE, localedir=config.localedir) 30 | 31 | __all__ = [ 'debugprint', 'get_debugging', 'set_debugging', 32 | 'Question', 33 | 'Multichoice', 34 | 'TEXT_start_print_admin_tool' ] 35 | 36 | TEXT_start_print_admin_tool = N_("To start this tool, select " 37 | "System->Administration->Print Settings " 38 | "from the main menu.") 39 | 40 | class Question: 41 | def __init__ (self, troubleshooter, name=None): 42 | self.troubleshooter = troubleshooter 43 | if name: 44 | self.__str__ = lambda: name 45 | 46 | def display (self): 47 | """Returns True if this page should be displayed, or False 48 | if it should be skipped.""" 49 | return True 50 | 51 | def connect_signals (self, handler): 52 | pass 53 | 54 | def disconnect_signals (self): 55 | pass 56 | 57 | def can_click_forward (self): 58 | return True 59 | 60 | def collect_answer (self): 61 | return {} 62 | 63 | def cancel_operation (self): 64 | pass 65 | 66 | ## Helper functions 67 | def initial_vbox (self, title='', text=''): 68 | vbox = Gtk.VBox () 69 | vbox.set_border_width (12) 70 | vbox.set_spacing (12) 71 | if title: 72 | s = '' + title + '\n\n' 73 | else: 74 | s = '' 75 | s += text 76 | label = Gtk.Label(label=s) 77 | label.set_alignment (0, 0) 78 | label.set_line_wrap (True) 79 | label.set_use_markup (True) 80 | vbox.pack_start (label, False, False, 0) 81 | return vbox 82 | 83 | class Multichoice(Question): 84 | def __init__ (self, troubleshooter, question_tag, question_title, 85 | question_text, choices, name=None): 86 | Question.__init__ (self, troubleshooter, name) 87 | page = self.initial_vbox (question_title, question_text) 88 | choice_vbox = Gtk.VBox () 89 | choice_vbox.set_spacing (6) 90 | page.pack_start (choice_vbox, False, False, 0) 91 | self.question_tag = question_tag 92 | self.widgets = [] 93 | button = None 94 | for choice, tag in choices: 95 | if button: 96 | button = Gtk.RadioButton.new_with_label_from_widget(button, choice) 97 | else: 98 | # special case to work around GNOME#635253 99 | button = Gtk.RadioButton.new_with_label([], choice) 100 | choice_vbox.pack_start (button, False, False, 0) 101 | self.widgets.append ((button, tag)) 102 | 103 | troubleshooter.new_page (page, self) 104 | 105 | def collect_answer (self): 106 | for button, answer_tag in self.widgets: 107 | if button.get_active (): 108 | return { self.question_tag: answer_tag } 109 | -------------------------------------------------------------------------------- /udev/.gitignore: -------------------------------------------------------------------------------- 1 | /udev-configure-printer.service 2 | -------------------------------------------------------------------------------- /udev/70-printers.rules: -------------------------------------------------------------------------------- 1 | # UDEV rules for USB devices - ENV variables can be monitored 2 | # via 'sudo udevadm monitor --udev --subsystem-match=usb --property' 3 | # Low-level USB device add trigger 4 | ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", ENV{ID_USB_INTERFACES}!="*:070104:*", TAG+="systemd", ENV{SYSTEMD_WANTS}="configure-printer@usb-$env{BUSNUM}-$env{DEVNUM}.service" 5 | # Low-level USB device remove trigger 6 | # Interface types: 7 | # 7/1/1 - device with unidirectional USB, managed by usb backend 8 | # 7/1/2 - device with bidirectional USB, managed by usb backend 9 | # 7/1/3 - device with special USB protocol, managed by hp backend 10 | # 7/1/4 - device with IPP over USB protocol, managed by ipp-usb daemon 11 | ACTION=="remove", SUBSYSTEM=="usb", ENV{INTERFACE}=="7/1/*", ENV{INTERFACE}!="7/1/4", RUN+="udev-configure-printer remove %p" 12 | -------------------------------------------------------------------------------- /udev/configure-printer@.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Configure Plugged-In Printer 3 | Requires=cups.socket 4 | After=cups.socket 5 | 6 | [Service] 7 | ExecStart=@udevdir@/udev-configure-printer add "%i" 8 | -------------------------------------------------------------------------------- /ui/AboutDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 5 8 | normal 9 | Copyright © 2006-2012 Red Hat, Inc. 10 | A CUPS configuration tool. 11 | https://github.com/OpenPrinting/system-config-printer/ 12 | 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. 13 | 14 | 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. 15 | 16 | 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. 17 | Florian Festi <ffesti@redhat.com> 18 | Tim Waugh <twaugh@redhat.com> 19 | Zdenek Dohnal <zdohnal@redhat.com> 20 | Till Kamppeter <till.kamppeter@gmail.com> 21 | translator-credits 22 | True 23 | 24 | 25 | False 26 | vertical 27 | 28 | 29 | False 30 | 31 | 32 | False 33 | True 34 | end 35 | 0 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ui/ConnectingDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 6 8 | Connecting to CUPS server 9 | False 10 | True 11 | center-on-parent 12 | dialog 13 | 14 | 15 | True 16 | False 17 | vertical 18 | 6 19 | 20 | 21 | True 22 | False 23 | end 24 | 25 | 26 | _Cancel 27 | True 28 | True 29 | True 30 | True 31 | False 32 | 33 | 34 | 35 | False 36 | False 37 | 0 38 | 39 | 40 | 41 | 42 | False 43 | True 44 | end 45 | 0 46 | 47 | 48 | 49 | 50 | True 51 | False 52 | 6 53 | vertical 54 | 6 55 | 56 | 57 | True 58 | False 59 | <span weight="bold" size="larger">Connecting to CUPS server</span> 60 | True 61 | 62 | 63 | False 64 | False 65 | 0 66 | 67 | 68 | 69 | 70 | True 71 | False 72 | 0.019999999553000001 73 | 74 | 75 | False 76 | False 77 | 1 78 | 79 | 80 | 81 | 82 | True 83 | False 84 | <i>Opening connection to %s</i> 85 | True 86 | 87 | 88 | False 89 | False 90 | 2 91 | 92 | 93 | 94 | 95 | False 96 | True 97 | 1 98 | 99 | 100 | 101 | 102 | 103 | button17 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /ui/InstallDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | False 8 | True 9 | center-on-parent 10 | dialog 11 | 12 | 13 | True 14 | False 15 | vertical 16 | 12 17 | 18 | 19 | True 20 | False 21 | end 22 | 23 | 24 | Close 25 | True 26 | True 27 | True 28 | True 29 | False 30 | 31 | 32 | False 33 | False 34 | 0 35 | 36 | 37 | 38 | 39 | True 40 | True 41 | True 42 | False 43 | 44 | 45 | True 46 | False 47 | 2 48 | 49 | 50 | True 51 | False 52 | gtk-yes 53 | 54 | 55 | False 56 | False 57 | 0 58 | 59 | 60 | 61 | 62 | True 63 | False 64 | _Install 65 | True 66 | 67 | 68 | False 69 | False 70 | 1 71 | 72 | 73 | 74 | 75 | 76 | 77 | False 78 | False 79 | 1 80 | 81 | 82 | 83 | 84 | False 85 | True 86 | end 87 | 0 88 | 89 | 90 | 91 | 92 | True 93 | False 94 | 6 95 | 12 96 | 97 | 98 | True 99 | False 100 | dialog-information 101 | 6 102 | 103 | 104 | True 105 | True 106 | 0 107 | 108 | 109 | 110 | 111 | True 112 | False 113 | label181 114 | True 115 | True 116 | 117 | 118 | False 119 | False 120 | 1 121 | 122 | 123 | 124 | 125 | False 126 | True 127 | 1 128 | 129 | 130 | 131 | 132 | 133 | button18 134 | button19 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /ui/JobsWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 200 7 | False 8 | Document Print Status 9 | 10 | 11 | 12 | True 13 | False 14 | vertical 15 | 16 | 17 | True 18 | False 19 | 20 | 21 | True 22 | False 23 | True 24 | Refresh job list 25 | _Refresh 26 | True 27 | view-refresh 28 | 29 | 30 | 31 | 32 | False 33 | True 34 | 35 | 36 | 37 | 38 | True 39 | False 40 | True 41 | Show completed jobs 42 | Show _completed jobs 43 | True 44 | gtk-ok 45 | 46 | 47 | 48 | False 49 | True 50 | 51 | 52 | 53 | 54 | True 55 | False 56 | 57 | 58 | False 59 | True 60 | 61 | 62 | 63 | 64 | False 65 | True 66 | 0 67 | 68 | 69 | 70 | 71 | True 72 | True 73 | never 74 | in 75 | 76 | 77 | True 78 | True 79 | True 80 | False 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | True 89 | True 90 | 1 91 | 92 | 93 | 94 | 95 | True 96 | False 97 | 98 | 99 | False 100 | False 101 | 2 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /ui/NewPrinterName.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | Duplicate Printer 8 | True 9 | center-on-parent 10 | dialog 11 | 12 | 13 | True 14 | False 15 | vertical 16 | 6 17 | 18 | 19 | True 20 | False 21 | end 22 | 23 | 24 | _Cancel 25 | True 26 | True 27 | True 28 | False 29 | 30 | 31 | False 32 | False 33 | 0 34 | 35 | 36 | 37 | 38 | _OK 39 | True 40 | True 41 | True 42 | True 43 | True 44 | False 45 | 46 | 47 | False 48 | False 49 | 1 50 | 51 | 52 | 53 | 54 | False 55 | True 56 | end 57 | 0 58 | 59 | 60 | 61 | 62 | True 63 | False 64 | New name for the printer 65 | 66 | 67 | False 68 | False 69 | 2 70 | 71 | 72 | 73 | 74 | True 75 | True 76 | True 77 | 78 | 79 | 80 | False 81 | False 82 | 3 83 | 84 | 85 | 86 | 87 | 88 | btnDuplicateCancel 89 | btnDuplicateOk 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /ui/SMBBrowseDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | SMB Browser 8 | True 9 | center-on-parent 10 | 11 | 12 | True 13 | False 14 | vertical 15 | 16 | 17 | 200 18 | True 19 | True 20 | 6 21 | in 22 | 23 | 24 | True 25 | True 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | True 37 | True 38 | 0 39 | 40 | 41 | 42 | 43 | True 44 | False 45 | 46 | 47 | False 48 | False 49 | 1 50 | 51 | 52 | 53 | 54 | True 55 | False 56 | end 57 | 6 58 | True 59 | 60 | 61 | _Refresh 62 | True 63 | True 64 | True 65 | True 66 | False 67 | 68 | 69 | 70 | False 71 | True 72 | 0 73 | 74 | 75 | 76 | 77 | _Cancel 78 | True 79 | True 80 | True 81 | True 82 | False 83 | 84 | 85 | 86 | False 87 | True 88 | 1 89 | 90 | 91 | 92 | 93 | _OK 94 | True 95 | True 96 | True 97 | True 98 | False 99 | 100 | 101 | 102 | False 103 | True 104 | 2 105 | 106 | 107 | 108 | 109 | False 110 | False 111 | 2 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /ui/WaitWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | Please Wait 8 | False 9 | True 10 | center-on-parent 11 | True 12 | 13 | 14 | True 15 | False 16 | 0 17 | out 18 | 19 | 20 | True 21 | False 22 | 12 23 | 6 24 | 12 25 | 26 | 27 | True 28 | False 29 | dialog-information 30 | 6 31 | 32 | 33 | True 34 | True 35 | 0 36 | 37 | 38 | 39 | 40 | True 41 | False 42 | label135 43 | True 44 | True 45 | 46 | 47 | False 48 | False 49 | 1 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /ui/remove-gtkalignment.xsl: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ui/statusicon_popupmenu.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 8 | 9 | True 10 | False 11 | _Hide 12 | True 13 | 14 | 15 | 16 | 17 | 18 | _Configure Printers 19 | True 20 | False 21 | True 22 | 23 | 24 | 25 | 26 | 27 | True 28 | False 29 | 30 | 31 | 32 | 33 | _Quit 34 | True 35 | True 36 | False 37 | True 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /xml/validate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2010 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ## This program performs validation that cannot be performed using 24 | ## RELAX NG alone. 25 | 26 | import fnmatch 27 | import sys 28 | import xml.etree.ElementTree 29 | 30 | class Validator: 31 | def __init__ (self, filename): 32 | self._filename = filename 33 | 34 | def validate (self): 35 | filename = self._filename 36 | print ("Validating %s" % filename) 37 | preferreddrivers = xml.etree.ElementTree.XML (open (filename).read ()) 38 | (drivertypes, preferenceorder) = list(preferreddrivers) 39 | validates = True 40 | 41 | names = set() 42 | for drivertype in list(drivertypes): 43 | name = drivertype.get ("name") 44 | names.add (name) 45 | 46 | for printer in list(preferenceorder): 47 | types = [] 48 | drivers = printer.find ("drivers") 49 | if drivers is not None: 50 | types.extend (list(drivers)) 51 | 52 | blacklist = printer.find ("blacklist") 53 | if blacklist is not None: 54 | types.extend (list(blacklist)) 55 | 56 | for drivertype in types: 57 | pattern = drivertype.text.strip () 58 | matches = fnmatch.filter (names, pattern) 59 | names -= set (matches) 60 | 61 | for name in names: 62 | validates = False 63 | print(("*** Driver type \"%s\" is never used" % 64 | name), file=sys.stderr) 65 | 66 | return validates 67 | 68 | import getopt 69 | import os 70 | opts, args = getopt.getopt (sys.argv[1:], "") 71 | 72 | if len (args) < 1: 73 | dirname = os.path.dirname (sys.argv[0]) 74 | args = [os.path.join (dirname, "preferreddrivers.xml")] 75 | 76 | exitcode = 0 77 | for filename in args: 78 | validator = Validator (filename) 79 | if not validator.validate (): 80 | exitcode = 1 81 | 82 | sys.exit (exitcode) 83 | --------------------------------------------------------------------------------