From 8c9bd5f708f09c10bc041c954ef8893cb460cb31 Mon Sep 17 00:00:00 2001 From: Rudra-IITM Date: Sat, 10 Aug 2024 11:50:12 +0530 Subject: [PATCH 01/13] Initial Repository Setup for Rockcrafting: - Added parts: - pappl - pappl-retrofit - qpdf - ghostscript - cups - libcupsfilters - libppd - cups-filters - pyppd - hplip - hplip-printer-app - avahi-daemon - scripts - dbus-scripts - Added service to start dbus-daemon and start hplip-printer-app server - Included scripts: - Scripts to start dbus-daemon - Makefiles and scripts to build ps-printer-app --- .gitignore | 1 + HP | 50 + LICENSE | 202 ++++ Makefile | 119 +++ NOTICE | 6 + README.md | 355 +++++++ hplip-printer-app.1 | 112 +++ hplip-printer-app.c | 1854 +++++++++++++++++++++++++++++++++++++ hplip-printer-app.service | 10 + rockcraft.yaml | 860 +++++++++++++++++ scripts/run-dbus.sh | 32 + testpage.ps | Bin 0 -> 17190 bytes 12 files changed, 3601 insertions(+) create mode 100644 .gitignore create mode 100755 HP create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 NOTICE create mode 100644 README.md create mode 100644 hplip-printer-app.1 create mode 100644 hplip-printer-app.c create mode 100644 hplip-printer-app.service create mode 100644 rockcraft.yaml create mode 100644 scripts/run-dbus.sh create mode 100644 testpage.ps diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..013c47c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.rock \ No newline at end of file diff --git a/HP b/HP new file mode 100755 index 0000000..69e776e --- /dev/null +++ b/HP @@ -0,0 +1,50 @@ +#!/bin/sh + +if ! which hp-probe >/dev/null 2>&1; then + echo "ERROR: \"hp-probe\" (HPLIP) not found" 1>&2 + exit 1 +fi +if ! hp-probe -h >/dev/null 2>&1; then + echo "ERROR: \"hp-probe\" (HPLIP) not executable (Is Python installed?)" 1>&2 + exit 1 +fi +if ! which perl >/dev/null 2>&1; then + echo "ERROR: \"perl\" not found" 1>&2 + exit 1 +fi +if ! perl -h >/dev/null 2>&1; then + echo "ERROR: \"perl\" not executable" 1>&2 + exit 1 +fi +if ! which avahi-resolve-address >/dev/null 2>&1; then + echo "WARNING: \"avahi-resolve-address\" not found, URIs will use IP address instead of ZeroConf name" 1>&2 +fi +if ! which host >/dev/null 2>&1; then + echo "WARNING: \"host\" utility not found, URIs will use IP address instead of host name" 1>&2 +fi + +for line in \ + `hp-probe -bnet -o5 | \ + grep 'hp:/net/' | \ + perl -p -e 's|^\s*(\S+)\s+(\S+)\s+(\S+)\s*$|URI=\1;MODELRAW=\2;NETID=\3\n|'`; do + eval "$line" + MODEL=`echo $MODELRAW | perl -p -e 's/_/ /g'` + DEVID=`echo $MODEL | perl -p -e 's/^(Hewlett[ -]Packard|\S+)\s+(.*)$/MFG:\1;MDL:\1 \2;/i'` + IP=`echo $URI | perl -p -e 's|^.+\?ip=(\d+\.\d+\.\d+\.\d+)\s*$|\1|i'` || IP= + if [ -n "$IP" ]; then + ZEROCONFNAME=`avahi-resolve-address $IP | perl -p -e 's/^\s*\S+\s+(\S+?)(\.local\.?)?\s*$/\1/'` || ZEROCONFNAME= + if [ -n "$ZEROCONFNAME" ]; then + URI=`echo $URI | perl -p -e "s|\?ip=(\d+\.\d+\.\d+\.\d+)|?zc=$ZEROCONFNAME|"` + else + HOSTNAME=`host $IP | head -1 | perl -p -e 's/^.*\s+(\S+?)\.?\s*$/\1/'` || : + if [ -n "$HOSTNAME" ]; then + URI=`echo $URI | perl -p -e "s|\?ip=(\d+\.\d+\.\d+\.\d+)|?hostname=$HOSTNAME|"` + fi + fi + fi + if [ -n "$URI" -a -n "$MODEL" -a -n "$NETID" -a -n "$DEVID" ]; then + echo network $URI \"$MODEL\" "\"$MODEL Network $NETID HPLIP\"" \"$DEVID\" \"\" + fi +done + +exit 0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..23bc06b --- /dev/null +++ b/Makefile @@ -0,0 +1,119 @@ +# +# Makefile for the HPLIP Printer Application +# +# Copyright © 2020-2021 by Till Kamppeter +# Copyright © 2020 by Michael R Sweet +# +# Licensed under Apache License v2.0. See the file "LICENSE" for more +# information. +# + +# Version and +VERSION = 1.0 +prefix = /usr +sysconfdir = /etc +localstatedir = /var +includedir = $(prefix)/include +bindir = $(prefix)/bin +libdir = $(prefix)/lib +mandir = $(prefix)/share/man +ppddir = $(prefix)/share/ppd +statedir = $(localstatedir)/lib/hplip-printer-app +spooldir = $(localstatedir)/spool/hplip-printer-app +serverbin = $(prefix)/lib/hplip-printer-app +resourcedir = $(prefix)/share/hplip-printer-app +cupsserverbin = `cups-config --serverbin` +unitdir = `pkg-config --variable=systemdsystemunitdir systemd` +HPLIP_CONF_DIR = $(sysconfdir)/hp +HPLIP_PLUGIN_STATE_DIR = $(localstatedir)/lib/hp + +# Compiler/linker options... +OPTIM = -Os -g +DIRS = -DHPLIP_CONF_DIR=\"$(HPLIP_CONF_DIR)\" -DHPLIP_PLUGIN_STATE_DIR=\"$(HPLIP_PLUGIN_STATE_DIR)\" +ifdef HPLIP_PLUGIN_ALT_DIR +DIRS += -DHPLIP_PLUGIN_ALT_DIR=\"$(HPLIP_PLUGIN_ALT_DIR)\" +endif +CFLAGS += `pkg-config --cflags pappl` `cups-config --cflags` `pkg-config --cflags libppd` `pkg-config --cflags libcupsfilters` `pkg-config --cflags libpappl-retrofit` `pkg-config --cflags libcurl` `pkg-config --cflags libcrypto` $(DIRS) $(OPTIM) +ifdef VERSION +CFLAGS += -DSYSTEM_VERSION_STR="\"$(VERSION)\"" +ifndef MAJOR +MAJOR = `echo $(VERSION) | perl -p -e 's/^(\d+).*$$/\1/'` +endif +ifndef MINOR +MINOR = `echo $(VERSION) | perl -p -e 's/^\d+\D+(\d+).*$$/\1/'` +endif +ifndef PATCH +PATCH = `echo $(VERSION) | perl -p -e 's/^\d+\D+\d+\D+(\d+).*$$/\1/'` +endif +ifndef PACKAGE +PACKAGE = `echo $(VERSION) | perl -p -e 's/^\d+\D+\d+\D+\d+\D+(\d+).*$$/\1/'` +endif +endif +ifdef MAJOR +CFLAGS += -DSYSTEM_VERSION_ARR_0=$(MAJOR) +endif +ifdef MINOR +CFLAGS += -DSYSTEM_VERSION_ARR_1=$(MINOR) +endif +ifdef PATCH +CFLAGS += -DSYSTEM_VERSION_ARR_2=$(PATCH) +endif +ifdef PACKAGE +CFLAGS += -DSYSTEM_VERSION_ARR_3=$(PACKAGE) +endif +ifdef SNAP +CFLAGS += -DSNAP=$(SNAP) +endif +LDFLAGS += $(OPTIM) `cups-config --ldflags` +LIBS += `pkg-config --libs pappl` `cups-config --image --libs` `pkg-config --libs libppd` `pkg-config --libs libcupsfilters` `pkg-config --libs libpappl-retrofit` `pkg-config --libs libcurl` `pkg-config --libs libcrypto` + + +# Targets... +OBJS = hplip-printer-app.o +TARGETS = hplip-printer-app + + +# General build rules... +.SUFFIXES: .c .o +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + + +# Targets... +all: $(TARGETS) + +clean: + rm -f $(TARGETS) $(OBJS) + +install: $(TARGETS) + mkdir -p $(DESTDIR)$(bindir) + cp $(TARGETS) $(DESTDIR)$(bindir) + mkdir -p $(DESTDIR)$(mandir)/man1 + cp hplip-printer-app.1 $(DESTDIR)$(mandir)/man1 + mkdir -p $(DESTDIR)$(ppddir) + mkdir -p $(DESTDIR)$(statedir)/ppd + mkdir -p $(DESTDIR)$(spooldir) + mkdir -p $(DESTDIR)$(resourcedir) + cp testpage.ps $(DESTDIR)$(resourcedir) + if test "x$(cupsserverbin)" != x && [ -d $(cupsserverbin) ]; then \ + mkdir -p $(DESTDIR)$(libdir); \ + touch $(DESTDIR)$(serverbin) 2> /dev/null || :; \ + if rm $(DESTDIR)$(serverbin) 2> /dev/null; then \ + ln -s $(cupsserverbin) $(DESTDIR)$(serverbin); \ + fi; \ + mkdir -p $(DESTDIR)$(cupsserverbin)/backend; \ + cp HP $(DESTDIR)$(cupsserverbin)/backend; \ + else \ + mkdir -p $(DESTDIR)$(serverbin)/filter; \ + mkdir -p $(DESTDIR)$(serverbin)/backend; \ + cp HP $(DESTDIR)$(serverbin)/backend; \ + fi + if test "x$(unitdir)" != x; then \ + mkdir -p $(DESTDIR)$(unitdir); \ + cp hplip-printer-app.service $(DESTDIR)$(unitdir); \ + fi + +hplip-printer-app: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) + +$(OBJS): Makefile diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..33449ca --- /dev/null +++ b/NOTICE @@ -0,0 +1,6 @@ +HPLIP Printer Application + +Copyright © 2020-2021 by Till Kamppeter. +Copyright © 2020 by Michael R Sweet. +Copyright © 2007-2019 by Apple Inc. +Copyright © 1997-2007 by Easy Software Products. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3aa5fe6 --- /dev/null +++ b/README.md @@ -0,0 +1,355 @@ +# HPLIP Printer Application + +## INTRODUCTION + +This repository contains a Printer Application for printing on HP and +Apollo printers supported by HP's HPLIP driver suite. + +It uses [PAPPL](https://www.msweet.org/pappl) to support IPP +printing from multiple operating systems. In addition, it uses the +resources of [cups-filters +2.x](https://github.com/OpenPrinting/cups-filters) (filter functions +in libcupsfilters, libppd) and +[pappl-retrofit](https://github.com/OpenPrinting/pappl-retrofit) +(encapsulating classic CUPS drivers in Printer Applications). This +work (or now the code of pappl-retrofit) is derived from the +[hp-printer-app](https://github.com/michaelrsweet/hp-printer-app). + +The printer driver itself and the software to communicate with the +printer hardware is taken from the [HPLIP (HP Linux Imaging and +Printing) driver +suite](https://developers.hp.com/hp-linux-imaging-and-printing/), also +the Information about supported printer models and their capabilities. + +Your contributions are welcome. Please post [issues and pull +requests](https://github.com/OpenPrinting/hplip-printer-app). + +**Note: HPLIP is actively maintained by HP, they are continuously +adding the newest printer models and adapting the software to new +environments/Linux distributions. Therefore it would also be the +correct way if HP turns HPLIP into a Printer Application or at least +offers this as an alternative to the classic CUPS/SANE +driver. Especially they should create a native Printer Application, +meaning that it does not use PPDs, CUPS filters, and CUPS backends +internally. Also their utilities need to be made independent of +CUPS.** + +For PostScript printers you can also use the [PostScript Printer +Application](https://github.com/OpenPrinting/ps-printer-app), +especially if you have it already installed for some non-HP PostScript +printer. + +Also check whether your printer is a driverless IPP printer (AirPrint, +Mopria, IPP Everywhere, Wi-Fi Direct Print, prints from phones) as in +this case you do not need any Printer Application at all. Most modern +HP printers, even the cheapest models, are driverless IPP +printers. Even USB-only printers can be driverless IPP, and you can +generally use driverless IPP via USB, try +[ipp-usb](https://github.com/OpenPrinting/ipp-usb) for these cases +first. + + +### Properties + +- A Printer Application providing the `hpcups` printer driver and all + printer's PPDs of HPLIP, supporting printing on most printers from + HP and Apollo. This allows easy printing in high quality, including + photos on photo paper. The `hpps` CUPS filter for PIN-protected + printing on PostScript printers is also included. + +- The printers are discovered with HPLIP, too. For USB printers the + `hp` CUPS backend is used and for network printers the `hp-probe` + utility (encapsulated in a script to behave as a CUPS backend). + +- The communication with the printers is done by the `hp` CUPS backend + and so (at least in case of USB) the IEEE-1284.4 packet protocol + (protocol 7/1/3 on USB) is used and not a simple stream protocol + (like the standard CUPS and PAPPL backends use). This way one should + be able to print and scan simultaneously, or at least check printer + status while printing. Not all printers support this protocol, if + not, a standard streaming protocol is used. Also any other special + functionality which requires the `hp` backend is supported. On the + "Add Printer" web interface page under "Devices" select the "HPLIP + (HP)" entries. + +- Note that the `hp` backend does not allow bi-directional access to + the printer. If you have a PostScript printer and prefer support for + remote querying of the printer's accessory configuration instead of + simultaneous printing and scanning, CUPS' standard backends for USB + and network printers are also available. + +- If you have an unusal system configuration or a personal firewall + HP's backends will perhaps not discover your printer. Also in this + situation the standard backends, including the fully manual "Network + Printer" entry in combination with the hostname/IP field can be + helpful. + +- Use of CUPS' instead of PAPPL's standard backends makes quirk + workarounds for USB printers with compatibility problems being used + (and are editable) and the output can get sent to the printer via + IPP, IPPS (encrypted!), and LPD in addition to socket (usually port + 9100). The SNMP backend can get configured (community, address + scope). + +- PWG Raster, Apple Raster or image input data to be printed on a + non-PostScript printer does not get converted to PostScript or PDF, + it is only converted/scaled to the required color space and + resolution and then fed into the `hpcups` driver. + +- For printing on non-PostScript printers PDF and PostScript input + data is rendered into raster data using Ghostscript. Ghostscript is + also used to convert PDF into PostScript for PostScript printers. + +- The information about which printer models are supported and which + are their capabilities is based on the PPD files included in + HPLIP. They are packaged in the Snap as a compressed archive. + +- Standard job IPP attributes are mapped to the driver's option + settings best fitting to them so that users can print from any type + of client (like for example a phone or IoT device) which only + supports standard IPP attributes and cannot retrive the PPD + options. Trays, media sizes, media types, and duplex can get mapped + easily, but when it comes to color and quality it gets more complex, + as relevant options differ a lot in the PPD files. Here we use an + algorithm which automatically (who wants hand-edit ~3000 PPD files + for the assignments) finds the right set of option settings for each + combination of `print-color-mode` (`color`/`monochrome`), + `print-quality` (`draft`/`normal`/`high`), and + `print-content-optimize` + (`auto`/`photo`/`graphics`/`text`/`text-and-graphics`) in the PPD of + the current printer. So you have easy access to the full quality or + speed of your printer without needing to deal with printer-specific + option settings (the original options are still accessible via web + admin interface). + +- The Snap of the HPLIP Printer Application takes HPLIP's source code + from Debian's packaging repository instead of directly from HP, as + Debian's package has ~80 patches fixing bugs which are reported to + HP but the patch not adopted upstream. So with the Snap users should + get the same experience in reliability and quality as with the + Debian package. + +- Support for downloading the proprietary plugin of HPLIP via an + additional page in the web interface. This adds support for some + laser printers which need their firmware loaded every time they are + turned on or which use certain proprietary print data formats. This + works both in the Snap and in the classic installation of the + Printer Application (must run as root, otherwise only status check + of the plugin). + +### To Do + +- Support for scanning on HP's multi-function printers. this requires + scanning support in PAPPL (which made [good progress in GSoC + 2021](https://github.com/michaelrsweet/pappl/commits/scanning)). + +- PDF test page, for example generated with the bannertopdf filter. + +- Human-readable strings for vendor options (Needs support by PAPPL: + [Issue #58: Localization + support](https://github.com/michaelrsweet/pappl/issues/58)) + +- Internationalization/Localization (Needs support by PAPPL: [Issue + #58: Localization + support](https://github.com/michaelrsweet/pappl/issues/58)) + +- SNMP Ink level check via ps_status() function (Needs support by PAPPL: + [Issue #83: CUPS does IPP and SNMP ink level polls via backends, + PAPPL should have functions for + this](https://github.com/michaelrsweet/pappl/issues/83)) + +- Build options for cups-filters, to build without libqpdf and/or + without libppd, the former will allow to create the Snap of this + Printer Application without downloading and building QPDF + + +## THE SNAP + +### Installing and building + +To just run and use this Printer Application, simply install it from +the Snap Store: + +``` +sudo snap install --edge hplip-printer-app +``` + +Then follow the instructions below for setting it up. + +To build the Snap by yourself, in the main directory of this +repository run + +``` +snapcraft snap +``` + +This will download all needed packages and build the HPLIP Printer +Application. Note that PAPPL (upcoming 1.0) and cups-filters (upcoming +2.0) are pulled directly from their GIT repositories, as there are no +appropriate releases yet. This can also lead to the fact that this +Printer Application will suddenly not build any more. + +To install the resulting Snap run + +``` +sudo snap install --dangerous hplip-printer-app_1.0_amd64.snap +``` + + +### Setting up + +The Printer Application will automatically be started as a server daemon. + +Enter the web interface + +``` +http://localhost:8000/ +``` + +Use the web interface to add a printer. Supply a name, select the +discovered printer, then select make and model. Also set the installed +accessories, loaded media and the option defaults. If the printer is a +PostScript printer, accessory configuration and option defaults can +also often get polled from the printer. + +If the entry of your printer in the web interface has the remark +"requires proprietary plugin", you need to install HP's plugin. For +this, click on the "Plugin" button in this printer entry or on the +"Install Proprietary Plugin" button under "Other Settings" on the +front page of the web interface and follow the instructions on the +screen. + +Then print PDF, PostScript, JPEG, Apple Raster, or PWG Raster files +with + +``` +hplip-printer-app FILE +``` + +or print with CUPS, CUPS (and also cups-browsed) discover and treat +the printers set up with this Printer Application as driverless IPP +printers (IPP Everywhere and AirPrint). + +See + +``` +hplip-printer-app --help +``` + +for more options. + +Use the "-o log-level=debug" argument for verbose logging in your +terminal window. + +You can add files to `/var/snap/hplip-printer-app/common/usb/` for +additional USB quirk rules. Edit the existing files only for quick +tests, as they get replaced at every update of the Snap (to introduce +new rules). + +You can edit the `/var/snap/hplip-printer-app/common/cups/snmp.conf` +file for configuring SNMP network printer discovery. + + +## BUILDING WITHOUT SNAP + +You can also do a "quick-and-dirty" build without snapping and without +needing to install [PAPPL](https://www.msweet.org/pappl), +[cups-filters 2.x](https://github.com/OpenPrinting/cups-filters), and +[pappl-retrofit](https://github.com/OpenPrinting/pappl-retrofit) into +your system. You need a directory with the latest GIT snapshot of +PAPPL, the latest GIT snapshot of cups-filters, and the latest GIT +snapshot of pappl-retrofit (master branches of each). They all need to +be compiled (`./autogen.sh; ./configure; make`), installing not +needed. Also install the header files of all needed libraries +(installing "libcups2-dev" should do it). + +In the directory with hplip-printer-app.c run the command line + +``` +gcc -o hplip-printer-app hplip-printer-app.c $PAPPL_SRC/pappl/libpappl.a $CUPS_FILTERS_SRC/.libs/libppd.a $CUPS_FILTERS_SRC/.libs/libcupsfilters.a $PAPPL_RETROFIT_SRC/.libs/libpappl-retrofit.a -ldl -lpthread -lppd -lcups -lavahi-common -lavahi-client -lgnutls -ljpeg -lpng16 -ltiff -lz -lm -lusb-1.0 -lpam -lqpdf -lstdc++ -I. -I$PAPPL_SRC/pappl -I$CUPS_FILTERS_SRC/ppd -I$CUPS_FILTERS_SRC/cupsfilters -I$PAPPL_RETROFIT_SRC/pappl/retrofit -L$CUPS_FILTERS_SRC/.libs/ -L$PAPPL_RETROFIT_SRC/.libs/ +``` + +There is also a Makefile, but this needs PAPPL, cups-filters 2.x, and +pappl-retrofit to be installed into your system. + +Run + +``` +./hplip-printer-app --help +``` + +When running the non-snapped version, by default, PPD files are +searched for in + +``` +/usr/share/ppd/ +/usr/lib/cups/driver/ +/var/lib/hplip-printer-app/ppd/ +``` + +You can set the `PPD_PATHS` environment variable to search other +places instead: + +``` +PPD_PATHS=/path/to/my/ppds:/my/second/place ./hplip-printer-app server +``` + +Simply put a colon-separated list of any amount of paths into the +variable. Creating a wrapper script is recommended. + +Note that only PPD files for the `hpcups` driver of HPLIP are +considred, other PPD files are ignored. + +Printers are only discovered via the `hp` backend of HPLIP (USB) or +the `hp-probe` utility of HPLIP (network). For the latter a wrapper +script named `HP` is included which makes the utility be used like a +CUPS backend (discovery mode only). This especially makes only HP and +Apollo printers being discovered. Printers from other manufacturers +are not supported. + +Jobs are filtered through `hpcups` and send to the printer via the +`hp` backend (both USB and network). + +The standard (not HPLIP's) backends provided as alternative in this +Printer Application are CUPS' backends and not PAPPL's, meaning that +for USB printers CUPS' USB quirk workarounds for compatibility +problems are used, network printers can also be used with IPP, IPPS, +and LPD protocols, and SNMP printer discovery is configurable. + +USB Quirk rules in `/usr/share/cups/usb` and the `/etc/cups/snmp.conf` +file can get edited if needed. + +Make sure you have HPLIP installed and if you want to use standard +backends, CUPS (at least its backends). + +You also need Ghostscript to print PDF or PostScript jobs. + +For access to the test page `testpage.ps` use the TESTPAGE_DIR +environment variable: + +``` +TESTPAGE_DIR=`pwd` PPD_PATHS=/path/to/my/ppds:/my/second/place ./hplip-printer-app server +``` + +or for your own creation of a test page (PostScript, PDF, PNG, JPEG, +Apple Raster, PWG Raster): + +``` +TESTPAGE=/path/to/my/testpage/my_testpage.ps PPD_PATHS=/path/to/my/ppds:/my/second/place ./hplip-printer-app server +``` + + +## LEGAL STUFF + +The HPLIP Printer Application is Copyright © 2020 by Till Kamppeter. + +It is derived from the HP PCL Printer Application, a first working model of +a raster Printer Application using PAPPL. It is available here: + +https://github.com/michaelrsweet/hp-printer-app + +The HP PCL Printer Application is Copyright © 2019-2020 by Michael R Sweet. + +This software is licensed under the Apache License Version 2.0 with an exception +to allow linking against GPL2/LGPL2 software (like older versions of CUPS). See +the files "LICENSE" and "NOTICE" for more information. diff --git a/hplip-printer-app.1 b/hplip-printer-app.1 new file mode 100644 index 0000000..181f0c3 --- /dev/null +++ b/hplip-printer-app.1 @@ -0,0 +1,112 @@ +.\" +.\" HPLIP Printer Application man page +.\" +.\" Copyright © 2020-2021 by Till Kamppeter +.\" Copyright © 2020 by Michael R Sweet +.\" +.\" Licensed under Apache License v2.0. See the file "LICENSE" for more +.\" information. +.\" +.TH hplip-printer-app 1 "hplip-printer-app" "2021-09-04" "OpenPrinting" +.SH NAME +hplip-printer-app \- HPLIP Printer Application +.SH SYNOPSIS +.B hplip-printer-app +[ +.I SUB-COMMAND +] [ OPTIONS ] [ FILES ] +.SH DESCRIPTION +.B hplip-printer-app +is a Printer Application that can be run standalone or as a dedicated IPP Everywhere network service. +.B hplip-printer-app +supports printing PDF files, PostScript files, JPEG images, PNG images, and Apple/PWG raster files to most USB and network printers from HP and Apollo. It is based on HP's HPLIP driver suite, using its `hpcups` and `hpps` CUPS print filters and the `hp` CUPS backend. For discovering network printers the `hp-probe` utility is used. + +It currently only supports printing. Scanning and loading the proprietary pluging from HP will get added later. I am not sure whether I will also include the utilities, both text and graphical interface. + +Note that HPLIP is actively maintained by HP. There is a new release every 2 or 3 months, but currently still only as classic CUPS and SANE drivers. It would be HP's task to switch their software over to be a Printer Application, or offer both modes. For the time being until this happens I will offer this Printer Application retro-fitting the current HPLIP releases. + +.SH SUB-COMMANDS +If no sub-command is specified, "submit" is assumed. + +The following sub-commands are recognized by +.B hplip-printer-app: +.TP 5 +add +Add a printer queue. +.TP 5 +cancel +Cancel one or more print jobs. +.TP 5 +default +Get/Set the default printer queue. +.TP 5 +delete +Delete a printer queue. +.TP 5 +devices +List connected printers. +.TP 5 +drivers +List the supported drivers. +.TP 5 +jobs +List pending print jobs. +.TP 5 +modify +Modify a printer queue. +.TP 5 +options +List supported options. +.TP 5 +printers +List the printer queues. +.TP 5 +server +Start a server. +.TP 5 +shutdown +Shutdown a running server. +.TP 5 +status +Show the status of a printer or all printers. +.TP 5 +submit +Submit a file for printing. +.SH OPTIONS +The following options are recognized by +.B hplip-printer-app: +.TP 5 +-a +Cancel all jobs. +.TP 5 +-d PRINTER +Specify printer. +.TP 5 +-h HOST +Specify hostname. +.TP 5 +-j JOB-ID +Specify job ID. +.TP 5 +-m DRIVER-NAME +Specify driver. +.TP 5 +-n COPIES +Specify number of copies. +.TP 5 +-o NAME=VALUE +Specify option. +.TP 5 +-t TITLE +Specify job title. +.TP 5 +-u URI +Specify ipp: or ipps: printer/server. +.TP 5 +-v DEVICE-URI +Specify socket: or usb: device. +.SH SEE ALSO +https://github.com/OpenPrinting/hplip-printer-app +.SH COPYRIGHT +Copyright \[co] 2020-2021 by Till Kamppeter. +Copyright \[co] 2020 by Michael R Sweet. diff --git a/hplip-printer-app.c b/hplip-printer-app.c new file mode 100644 index 0000000..2d37ed1 --- /dev/null +++ b/hplip-printer-app.c @@ -0,0 +1,1854 @@ +// +// HPLIP (hpcups) Printer Application based on PAPPL and libpappl-retrofit +// +// Copyright © 2020-2021 by Till Kamppeter. +// Copyright © 2020 by Michael R Sweet. +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +// +// Include necessary headers... +// + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Constants... +// + +// Name and version + +#define SYSTEM_NAME "HPLIP Printer Application" +#define SYSTEM_PACKAGE_NAME "hplip-printer-app" +#ifndef SYSTEM_VERSION_STR +# define SYSTEM_VERSION_STR "1.0" +#endif +#ifndef SYSTEM_VERSION_ARR_0 +# define SYSTEM_VERSION_ARR_0 1 +#endif +#ifndef SYSTEM_VERSION_ARR_1 +# define SYSTEM_VERSION_ARR_1 0 +#endif +#ifndef SYSTEM_VERSION_ARR_2 +# define SYSTEM_VERSION_ARR_2 0 +#endif +#ifndef SYSTEM_VERSION_ARR_3 +# define SYSTEM_VERSION_ARR_3 0 +#endif +#define SYSTEM_WEB_IF_FOOTER "Copyright © 2021 by Till Kamppeter. Provided under the terms of the Apache License 2.0." + +// Test page + +#define TESTPAGE "testpage.pdf" + +// System architecture + +#if defined(__x86_64__) || defined(_M_X64) + #define ARCH "x86_64" +#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) + #define ARCH "x86_32" +#elif defined(__aarch64__) || defined(_M_ARM64) || defined(__ARM_ARCH_ISA_A64) + #define ARCH "arm64" +#elif defined(__arm__) || defined(_M_ARM) + #define ARCH "arm32" +#else + // "grep"ping the binary should not match this + #define ARCH "UNSUPPORTED_ARCH" +#endif + +// Plugin download URLs + +#define PLUGIN_CONF_URL "http://hplip.sf.net/plugin.conf" +#define PLUGIN_ALT_LOCATION "https://developers.hp.com/sites/default/files" + + +// +// Types... +// + +typedef enum hplip_plugin_status_e +{ + HPLIP_PLUGIN_NOT_INSTALLED = 0, + HPLIP_PLUGIN_OUTDATED, + HPLIP_PLUGIN_INSTALLED +} hplip_plugin_status_t; + + +// +// Functions... +// + +// +// 'get_config_value()' - Return the value of a given variable/key in +// a given section of a config file +// + +char * +get_config_value(FILE *fp, + const char *section, + const char *key) +{ + char line[1024]; + char *value = NULL; + int in_section = 0; + + if (!key || !key[0]) + return (NULL); + + rewind(fp); + + while (fgets(line, sizeof(line), fp)) + { + while (line[strlen(line) - 1] == '\n' || + line[strlen(line) - 1] == '\r') // Remove newline + line[strlen(line) - 1] = '\0'; + + if (line[0] == '[') + { + if (section && !strncasecmp(line + 1, section, strlen(section)) && + line[strlen(section) + 1] == ']') + in_section = 1; + else + in_section = 0; + } + else if ((!section || in_section) && + !strncasecmp(line, key, strlen(key))) + { + value = line + strlen(key); + while (*value && isspace(*value)) value ++; + if (*value == '=') + { + value ++; + while (*value && isspace(*value)) value ++; + if (*value) + { + value = strdup(value); + break; + } + else + value = NULL; + } + else + value = NULL; + } + } + + return (value); +} + + +// +// 'set_config_value()' - Set a new value for a given variable/key in +// a given section of a config file. Create the +// section/key if not yet present. +// + +int +set_config_value(char **filebuf, + const char *section, + const char *key, + const char *value) +{ + char line[1024]; + int section_found = 0, + line_changed = 0, + file_changed = 0, + done = 0; + char *filebufptr, *buf, *bufptr, *lineptr, *lineendptr; + size_t size_needed, + bytes_written; + + if (!filebuf || !key || !key[0]) + return (0); + + size_needed = (*filebuf ? strlen(*filebuf) : 0) + + (section ? strlen(section) : 0) + + strlen(key) + + (value ? strlen(value) : 0) + 8; + + // Create a buffer for the whole file plus the new entry + buf = (char *)calloc(size_needed, sizeof(char)); + bufptr = buf; + + // Read the lines of the file to find the point where to apply the change + // and put the lines including the change into the buffer + if (*filebuf) + { + filebufptr = *filebuf; + while (*filebufptr) + { + // Read one line from input buffer + for (line[0] = '\0', lineptr = line; + *filebufptr && *filebufptr != '\n'; + filebufptr++, lineptr ++) + *lineptr = *filebufptr; + *lineptr = '\n'; + lineptr ++; + *lineptr = '\0'; + if (*filebufptr) filebufptr ++; + line_changed = 0; + + if (strlen(line) > 0 && !done) + { + // We have not yet set the requested value, still searching the + // right place in the file ... + if (line[0] == '[') + { + // New section + if (section && !strncasecmp(line + 1, section, strlen(section)) && + line[strlen(section) + 1] == ']') + // Start of requested section + section_found = 1; + else if (section_found && !done && value) + { + // Requested section has ended and new setting not yet written, + // Create new line at end of section + bytes_written = sprintf(bufptr, "%s = %s\n", key, value); + bufptr += bytes_written; + file_changed = 1; + done = 1; + } + } + else if ((!section || section_found) && + !strncasecmp(line, key, strlen(key))) + { + // Line begins with the name of the key we want to change, continue + // parsing + lineptr = line + strlen(key); + while (*lineptr && isspace(*lineptr)) lineptr ++; + if (*lineptr == '=' || *lineptr == '\n' || *lineptr == '\r' || + *lineptr == '\0') + { + // The line actually sets our key, right after the key name is + // an '=' ot the line end + if (value) // value == NULL removes the key in the section + { + // We have a valiue, so we change the key's value to hours + if (*lineptr == '=') + { + // Find start of value in line + lineptr ++; + while (*lineptr && isspace(*lineptr)) lineptr ++; + } + // Find end of value in line + lineendptr = lineptr; + while (*lineendptr && *lineendptr != '\n' && *lineendptr != '\r') + lineendptr ++; + if (strlen(value) != lineendptr - lineptr || + strncmp(value, lineptr, lineendptr - lineptr)) + { + // Value differs from the current one, only then write the + // line with the value replaced by hours + sprintf(bufptr, "%s", line); + bufptr += (lineptr - line); + bytes_written = sprintf(bufptr, "%s%s", value, lineendptr); + bufptr += bytes_written; + line_changed = 1; + file_changed = 1; + } + } + else + { + // Value is NULL, meaning that we want to remove the key, so + // mark the line as changed without writing it + line_changed = 1; + file_changed = 1; + } + done = 1; + } + } + } + if (!line_changed) + { + // No change needed on original line, write it as it is + bytes_written = sprintf(bufptr, "%s", line); + bufptr += bytes_written; + } + } + } + + if (!done && value) + { + // Requested section or requested key in section not found, create + // the section and the line in it + if (section && !section_found) + { + // Write section line + bytes_written = sprintf(bufptr, "[%s]\n", section); + bufptr += bytes_written; + } + // Write key=value line + bytes_written = sprintf(bufptr, "%s = %s\n", key, value); + bufptr += bytes_written; + file_changed = 1; + } + *bufptr = '\0'; + + if (file_changed) + { + // File has changed, replace the input buffer by the output buffer + if (*filebuf) + free(*filebuf); + *filebuf = buf; + } + else + free(buf); + + return (file_changed); +} + + +// +// 'hplip_version()' - Read out the HPLIP version from /etc/hp/hplip.conf +// + +char * +hplip_version(pappl_system_t *system) +{ + char buf[1024]; + FILE *fp; + char *version = NULL; + + + snprintf(buf, sizeof(buf), "%s/%s", HPLIP_CONF_DIR, "hplip.conf"); + if ((fp = fopen(buf, "r")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to open HPLIP configuration file %s", buf); + return (NULL); + } + + version = get_config_value(fp, "hplip", "version"); + + fclose(fp); + + return (version); +} + + +// +// 'hplip_plugin_status()' - Read out the status of the installed p;lugin +// from /var/lib/hp/hplip.state +// + +hplip_plugin_status_t +hplip_plugin_status(pappl_system_t *system) +{ + char buf[1024]; + FILE *fp; + char *plugin_version, + *installed_status, + *version; + hplip_plugin_status_t status = HPLIP_PLUGIN_NOT_INSTALLED; + + + snprintf(buf, sizeof(buf), "%s/%s", HPLIP_PLUGIN_STATE_DIR, "hplip.state"); + if ((fp = fopen(buf, "r")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to open HPLIP plugin status file %s", buf); + return (status); + } + + // HACK FOR TESTING: If empty hplip.state is created, install the plugin + // also if it was not installed before + fseek(fp, 0L, SEEK_END); + if (ftell(fp) == 0) + { + fclose(fp); + return (HPLIP_PLUGIN_OUTDATED); + } + + plugin_version = get_config_value(fp, "plugin", "version"); + installed_status = get_config_value(fp, "plugin", "installed"); + + if (installed_status && atoi(installed_status) != 0 && + plugin_version && plugin_version[0] && + (version = hplip_version(system)) != NULL) + { + if (!strcasecmp(plugin_version, version)) + status = HPLIP_PLUGIN_INSTALLED; + else + status = HPLIP_PLUGIN_OUTDATED; + free(version); + } + + if (plugin_version) + free(plugin_version); + if (installed_status) + free(installed_status); + fclose(fp); + + return (status); +} + + +// +// 'hplip_download_file() - Download a file from a given URL to a local +// temporary file +// + +char* +hplip_download_file(pappl_system_t *system, const char *url) +{ + int fd, status; + char tempfile[1024] = ""; + CURL *curl; + FILE *fp = NULL; + CURLcode ret; + + + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Downloading %s", url); + + // Setup curl + curl = curl_easy_init(); + if (curl) + { + // Create a temporary file + fd = cupsTempFd(tempfile, sizeof(tempfile)); + if (fd < 0 || !tempfile[0]) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to create temporary file"); + return (NULL); + } + + // Download the file + fp = fdopen(fd, "wb"); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 10L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + ret = curl_easy_perform(curl); + curl_easy_cleanup(curl); + fclose(fp); + close(fd); + } + else + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to start curl"); + return (NULL); + } + + // Check for errors + if ((int)ret == 0) + return(strdup(tempfile)); + else + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Download status: %d", ret); + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to download file %s", url); + unlink(tempfile); + } + + return (NULL); +} + + +// +// 'hplip_run_command_line()' - Run a command line and log its screen output, +// both stdout and stderr. Return the exit code +// of the command. +// + +int +hplip_run_command_line(pappl_system_t *system, const char *command) +{ + FILE *fp; + char buf[1024]; + + + if ((fp = popen(command, "r")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to start \"gpg\" utility fo plugin signature verification. Command: %s", + command); + return (-1); + } + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Executing command: %s", command); + + while (fgets(buf, sizeof(buf), fp) != NULL) + { + buf[strlen(buf) - 1] = '\0'; // Remove newline + papplLog(system, PAPPL_LOGLEVEL_DEBUG, " %s", buf); + } + + return (pclose(fp)); +} + + +// +// 'hplip_get_uncompress_dir() - Determine (and create) the directory +// where the plugin file should be +// uncompressed after download +// + +char * +hplip_get_uncompress_dir(pappl_system_t *system, int create) +{ +#ifdef HPLIP_PLUGIN_ALT_DIR + + (void)system; + (void)create; + + return strdup(HPLIP_PLUGIN_ALT_DIR); + +#else + + char buf[1024], + *home; + + + if ((home = getenv("HOME")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "No HOME environment variable defined."); + return (NULL); + } + + snprintf(buf, sizeof(buf), "%s/.hplip", home); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Directory to uncompress the plugin in: %s", buf); + + if (create) + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Creating directory for uncompressed plugin %s", buf); + if (mkdir(buf, S_IRWXU) == -1) + { + if (errno != EEXIST) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Error creating directory %s: %s", buf, strerror(errno)); + return (NULL); + } + } + } + + return (strdup(buf)); + +#endif // HPLIP_PLUGIN_ALT_DIR +} + + +// +// 'hplip_remove_uncompress_dir() - Remove the uncompressed plugin +// file when we do not need it any more +// + +int +hplip_remove_uncompress_dir(pappl_system_t *system, const char *name) +{ + char *uncompress_dir; + char buf[1024]; + DIR *d; + struct dirent *entry; + char *filename; + int len; + + + // Find the directory where the uncompressed plugin is located + if ((uncompress_dir = hplip_get_uncompress_dir(system, 0)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not determine the directory with the uncompressed plugin file."); + return (0); + } + + // Open the directory with the files of the uncompressed plugin + len = snprintf(buf, sizeof(buf), "%s/%s", uncompress_dir, name); + if ((d = opendir(buf)) == NULL && errno != ENOENT) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not open the directory %s: %s", buf, strerror(errno)); + free(uncompress_dir); + return (0); + } + + // Remove the files of the plugin (only regular files and symlinks, no + // sub-directories) + if (d) + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Removing uncompressed plugin directory %s", buf); + while (entry = readdir(d)) + { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + snprintf(buf + len, sizeof(buf) - len, "/%s", entry->d_name); + if (unlink(buf) != 0) + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not remove file %s: %s", buf, strerror(errno)); + } + closedir(d); + + // Remove the emptied directory + buf[len] = '\0'; + if (rmdir(buf) != 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not remove directory %s: %s", buf, strerror(errno)); + free(uncompress_dir); + return (0); + } + } + +#ifndef HPLIP_PLUGIN_ALT_DIR + + // Try to remove the directory in which we had uncompressed the + // plugin, but ignore errors (for example if it contains something + // else, then we most probably did not create it in the first place) + rmdir(uncompress_dir); + if (errno == 0) + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Removed directory for uncompressed plugin %s", uncompress_dir); + +#endif // !HPLIP_PLUGIN_ALT_DIR + + free(uncompress_dir); + return (1); +} + + +// +// 'hplip_download_plugin()' - Download the plugin from the HP's official +// locations, validate, and uncompress it +// + +char * +hplip_download_plugin(pappl_system_t *system) +{ + int i; + char *plugin_conf = NULL, + *plugin_file = NULL, + *signature_file = NULL, + *version = NULL, + *url = NULL, + *size_str = NULL, + *checksum = NULL, + *uncompress_dir = NULL, + *ret = NULL; + FILE *fp; + size_t plugin_size; + char buf[1024]; + struct stat st; + int fd; + int bytes; + SHA_CTX ctx; + unsigned char hash[SHA_DIGEST_LENGTH]; + int status; + + + // Get plugin index file from HP + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Getting plugin index from HP ..."); + if ((plugin_conf = hplip_download_file(system, PLUGIN_CONF_URL)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to download plugin index"); + goto out; + } + + // HPLIP version which we have installed, equals section name in the plugin + // index + if ((version = hplip_version(system)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to determine version of HPLIP"); + goto out; + } + + // Open downloaded plugin index + if ((fp = fopen(plugin_conf, "r")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to open downloaded plugin index file"); + goto out; + } + + // Read needed data items: URL, size, checksum + if ((url = get_config_value(fp, version, "url")) == NULL || !url[0]) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not find plugin URL in index file. Is HPLIP %s already released?", + version); + goto out; + } + + if ((size_str = get_config_value(fp, version, "size")) == NULL || + !size_str[0] || (plugin_size = atoi(size_str)) <= 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not find plugin size in index file."); + goto out; + } + + if ((checksum = get_config_value(fp, version, "checksum")) == NULL || + !checksum[0]) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not find plugin checksum in index file."); + goto out; + } + + fclose(fp); + + // Download the plugin file + buf[0] = '\0'; + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Getting plugin file for HPLIP %s from official location ...", + version); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Trying URL: %s", url); + if ((plugin_file = hplip_download_file(system, url)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Unable to download plugin file, trying backup server"); + snprintf(buf, sizeof(buf) - 5, "%s/hplip-%s-plugin.run", + PLUGIN_ALT_LOCATION, version); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Trying URL: %s", buf); + if ((plugin_file = hplip_download_file(system, buf)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to download plugin file."); + goto out; + } + } + + // Download the plugin signature file + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Getting plugin signature file from the same location as the plugin file ..."); + if (buf[0]) + strcpy(buf + strlen(buf), ".asc"); + else + snprintf(buf, sizeof(buf), "%s.asc", url); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Using URL: %s", buf); + if ((signature_file = hplip_download_file(system, buf)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to download plugin signature file."); + goto out; + } + + // Determine size of the downloaded plugin + stat(plugin_file, &st); + if (st.st_size != plugin_size) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Downloaded plugin file is not of expected size. File has %ld bytes but expected are %ld bytes.", + st.st_size, plugin_size); + goto out; + } + else + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Downloaded file size OK (%ld).", st.st_size); + + // Verify sha1sum of the plugin file against the checksum from the index file + SHA1_Init(&ctx); + fd = open(plugin_file, O_RDONLY); + while ((bytes = read(fd, buf, sizeof(buf))) > 0) + SHA1_Update(&ctx, buf, bytes); + close(fd); + SHA1_Final(hash, &ctx); + for (i = 0; i < SHA_DIGEST_LENGTH; i ++) + snprintf(buf + 2 * i, 3, "%.2x", hash[i]); + buf[2 * SHA_DIGEST_LENGTH] = '\0'; + if (strcasecmp(buf, checksum)) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Checksum of the plugin file (%s) does not match checksum of the plugin index (%s).", + buf, checksum); + goto out; + } + else + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Downloaded file checksum OK (%s).", buf); + + // Check GPG signature + + // We do not load HP's public key here (which is needed ofr verification), as + // HP's original command does not work. + // Command as of HPLIP 3.21.8: + // gpg --homedir ~ --no-permission-warning --keyserver pgp.mit.edu --recv-keys 0x4ABA2F66DBD5A95894910E0673D770CDA59047B9 + // So the actual command depends on the source code in use and how it got + // patched. For Debian/Ubuntu it is (key got added to the package): + // gpg --homedir ~ --no-permission-warning --import /usr/share/hplip/signing-key.asc + // Please add a suitable command to the start-up script for this Printer Application + + snprintf(buf, sizeof(buf), + "gpg --homedir ~ --no-permission-warning --verify %s %s 2>&1", + signature_file, plugin_file); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Verifying plugin's signature."); + + if ((status = hplip_run_command_line(system, buf)) != 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Plugin signature verification failed."); + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "NOTE: If this failure is due to a missing public key, we do not load the"); + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "public key here as this step can vary with the used HPLIP source code."); + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "The command used in the original source code of HPLIP (3.21.8)"); + papplLog(system, PAPPL_LOGLEVEL_ERROR, + " gpg --homedir ~ --no-permission-warning --keyserver pgp.mit.edu --recv-keys 0x4ABA2F66DBD5A95894910E0673D770CDA59047B9"); + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "does not work."); + goto out; + } + + // Get the directory where to uncompress the plugin + if ((uncompress_dir = hplip_get_uncompress_dir(system, 1)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not find/create the directory for uncompressing the plugin."); + goto out; + } + + // Make sure we have the directory name "plugin_tmp" free for uncompressing + // our plugin + if (!hplip_remove_uncompress_dir(system, "plugin_tmp")) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Old uncompressed plugin file is in the way: %s/plugin_tmp", + uncompress_dir); + goto out; + } + + // Uncompress the plugin + snprintf(buf, sizeof(buf), + "cd %s 2>&1 && mkdir plugin_tmp 2>&1 && cd plugin_tmp 2>&1 && sh %s --tar xf --no-same-owner 2>&1 && cd .. 2>&1 && chmod -R go+rX plugin_tmp 2>&1", + uncompress_dir, plugin_file); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Uncompressing the plugin file."); + + if ((status = hplip_run_command_line(system, buf)) != 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to uncompress the plugin"); + goto out; + } + + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Downloaded, verified, and uncompressed the plugin successfully. Ready to install in %s/plugin_tmp", + uncompress_dir); + + // Output the uncompress location + ret = uncompress_dir; + + out: + + // Clean up + if (plugin_conf) + { + unlink(plugin_conf); + free(plugin_conf); + } + if (signature_file) + { + unlink(signature_file); + free(signature_file); + } + if (version) + free(version); + if (url) + free(url); + if (size_str) + free(size_str); + if (checksum) + free(checksum); + if (plugin_file) + { + unlink(plugin_file); + free(plugin_file); + } + if (ret == NULL && uncompress_dir) + { + hplip_remove_uncompress_dir(system, "plugin_tmp"); + free(uncompress_dir); + } + + return (ret); +} + + +#ifdef SNAP +// +// 'hplip_register_plugin() - Register the plugin installation or +// removal in the hplip,state file (in the +// Snap only). +// + +int +hplip_register_plugin(pappl_system_t *system, const char *installed, + const char *eula, const char *version) +{ + int ret = 0; + char buf[1024]; + char *filebuf = NULL; + int size_needed; + FILE *fp; + + + // Register installation or removal of plugin in hplip.state + // Open current file, if present + snprintf(buf, sizeof(buf), "%s/%s", HPLIP_PLUGIN_STATE_DIR, "hplip.state"); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Registering plugin installation status in %s", buf); + if ((fp = fopen(buf, "r")) == NULL && errno != ENOENT) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to open HPLIP plugin status file %s: %s", + buf, strerror(errno)); + goto out; + } + + if (fp) + { + // Load complete file into a buffer (if we have a state file) + fseek(fp, 0L, SEEK_END); + size_needed = ftell(fp); + filebuf = (char *)calloc(size_needed + 1, sizeof(char)); + rewind(fp); + if (fread(filebuf, 1, size_needed, fp) != size_needed) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to read HPLIP plugin status file %s: %s", + buf, strerror(errno)); + fclose(fp); + goto out; + } + fclose(fp); + filebuf[size_needed] = '\0'; + } + + // Modify the values in the buffer + if (set_config_value(&filebuf, "plugin", "installed", installed) + + set_config_value(&filebuf, "plugin", "eula", eula) + + set_config_value(&filebuf, "plugin", "version", version) > 0) + { + // File has changed, rewrite it + // Remove the old file + if (unlink(buf) != 0 && errno != ENOENT) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to delete old HPLIP plugin status file %s: %s", + buf, strerror(errno)); + free(filebuf); + goto out; + } + + // Write new file + if ((fp = fopen(buf, "w")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to open HPLIP plugin status file %s: %s", + buf, strerror(errno)); + free(filebuf); + goto out; + } + if (fwrite(filebuf, 1, strlen(filebuf), fp) != strlen(filebuf)) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to write to HPLIP plugin status file %s: %s", + buf, strerror(errno)); + free(filebuf); + fclose(fp); + goto out; + } + + fclose(fp); + } + + // Done + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Registered plugin installation status successfully."); + ret = 1; + + out: + + return (ret); +} +#endif // SNAP + + +// +// 'hplip_install_plugin() - Install the downloaded plugin, after the +// license got accepted in the web +// interface, or right after uncompressing +// during an update. Use HP's Python script +// installPlugin.py contained in the plugin +// for a classic HPLIP installation and +// simply rename the plugin_tmp directory to +// plugin (deleting any old plugin/ +// directory first) and synlink the dynamic +// link library (*.so) files of the system's +// architecture for the Snap. The Printer +// Application must run as root to install +// the plugin. +// + +int +hplip_install_plugin(pappl_system_t *system, const char *plugin_dir) +{ + int ret = 0; + +#ifdef SNAP + + int len; + char buf1[1024], buf2[1024]; + DIR *d; + struct dirent *entry; + char *p, *version, *filebuf = NULL; + int size_needed; + FILE *fp; + + // Open the directory with the files of the uncompressed plugin + len = snprintf(buf1, sizeof(buf1), "%s/plugin_tmp", plugin_dir); + if ((d = opendir(buf1)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not open the directory %s: %s", buf1, strerror(errno)); + goto out; + } + + // Go through all the files of the plugin, and for the dynamic link + // libraries (*.so files) link the ones of our system's architecture + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Adding symlinks for dynamic link libraries of the %s architecture in %s", ARCH, buf1); + buf1[len] = '/'; + len ++; + while (entry = readdir(d)) + { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + if ((p = strstr(entry->d_name, ARCH)) != NULL && + p > entry->d_name && *(p - 1) == '-' && + strncmp(p + strlen(ARCH), ".so", 3) == 0) + { + memmove(buf1 + len, entry->d_name, (p - entry->d_name) - 1); + memmove(buf1 + len + (p - entry->d_name) - 1, p + strlen(ARCH), + strlen(p + strlen(ARCH)) + 1); + if (symlink(entry->d_name, buf1) != 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not create symboiic link %s to %s: %s", + buf1, entry->d_name, strerror(errno)); + goto out; + } + } + } + closedir(d); + + // Remove a previous version of the plugin + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Removing previous plugin version if present: %s/plugin", + plugin_dir); + if (hplip_remove_uncompress_dir(system, "plugin") == 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to remove old plugin version %s/plugin", plugin_dir); + goto out; + } + + // Rename the plugin directory from plugin_tmp to plugin + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Renaming plugin directory to %s/plugin", plugin_dir); + snprintf(buf1, sizeof(buf1), "%s/plugin_tmp", plugin_dir); + snprintf(buf2, sizeof(buf2), "%s/plugin", plugin_dir); + if (rename(buf1, buf2) != 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not rename directory %s to %s: %s", + buf1, buf2, strerror(errno)); + goto out; + } + + // Get HPLIP (and now also plugin) version + if ((version = hplip_version(system)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to determine version of HPLIP"); + goto out; + } + + // Register installed plugin version in hplip.state + if (!hplip_register_plugin(system, "1", "1", version)) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to register HPLIP plugin installation status."); + free(version); + goto out; + } + + free(version); + +#else + + char buf[1024]; + + // Run HP's script (included in the plugin) to install the plugin + snprintf(buf, sizeof(buf), + "cd %s/plugin_tmp 2>&1 && python3 installPlugin.py 2>&1", + plugin_dir); + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Installing the plugin."); + + if (hplip_run_command_line(system, buf) != 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to install the plugin"); + goto out; + } + +#endif // SNAP + + // Done + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Installed the plugin successfully."); + ret = 1; + + out: + + // Remove the uncompressed plugin file + hplip_remove_uncompress_dir(system, "plugin_tmp"); + + return (ret); +} + + +#ifdef SNAP +// +// 'hplip_remove_plugin() - Uninstall the installed plugin (in the +// Snap only), by removing its directory and +// unregistering its presence in the plugin +// state file. +// + +int +hplip_remove_plugin(pappl_system_t *system, const char *plugin_dir) +{ + int ret = 0; + char buf[1024]; + char *filebuf = NULL; + int size_needed; + FILE *fp; + + + // Remove the plugin directory + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Removing plugin directory %s/plugin", + plugin_dir); + if (hplip_remove_uncompress_dir(system, "plugin") == 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to remove plugin directory %s/plugin", plugin_dir); + goto out; + } + + // Register removal of plugin in hplip.state + if (!hplip_register_plugin(system, "0", NULL, NULL)) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to register HPLIP plugin installation status."); + goto out; + } + + // Done + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Uninstalled the plugin successfully."); + ret = 1; + + out: + + return (ret); +} +#endif // SNAP + + +// +// 'hplip_web_plugin()' - Web interface page for installing, updating, +// and removing HP's proprietary plugin. +// + +void +hplip_web_plugin( + pappl_client_t *client, // I - Client + void *data) // I - Global data +{ + pr_printer_app_global_data_t *global_data = + (pr_printer_app_global_data_t *)data; + pappl_system_t *system = prGetSystem(global_data); // System + const char *status = NULL; // Status text, if any + const char *uri = NULL; // Client URI + pappl_version_t version; + hplip_plugin_status_t plugin_status; + char *plugin_dir = NULL; + char buf[2048]; + char *licensetext = NULL; + FILE *fp; + size_t size_needed; + + + if (!papplClientHTMLAuthorize(client)) + return; + + // Get status of installed plugin + plugin_status = hplip_plugin_status(system); + + // Handle POSTs to add and delete PPD files... + if (papplClientGetMethod(client) == HTTP_STATE_POST) + { + int num_form = 0; // Number of form variables + cups_option_t *form = NULL; // Form variables + const char *action; // Form action + + if ((num_form = papplClientGetForm(client, &form)) == 0) + { + status = "Invalid form data."; + } + else if (!papplClientIsValidForm(client, num_form, form)) + { + status = "Invalid form submission."; + } + else if ((action = cupsGetOption("action", num_form, form)) == NULL) + { + status = "Missing action."; + } + else if (!strcmp(action, "install-plugin") || + !strcmp(action, "install-plugin-yes") || + !strcmp(action, "license-accepted")) + { + // Set status to trigger the "Are you sure?" page when we + // re-install over an already installed and ip-to-date plugin + status = "Installing plugin"; + if (strcmp(action, "license-accepted") && + (plugin_status != HPLIP_PLUGIN_INSTALLED || + !strcmp(action, "install-plugin-yes"))) + { + // Download the plugin + // Plugin installation only works if we are running as root + if (!getuid()) + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Downloading the proprietary plugin ..."); + if ((plugin_dir = hplip_download_plugin(system)) == NULL) + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to download plugin ..."); + else + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Plugin downloaded to %s", plugin_dir); + } + else + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Printer Application must run as root to download/install plugin."); + if (plugin_dir) + // Succeeded, set status so that if no installation follows now + // we get onto the license page (if plugin_status == + // HPLIP_PLUGIN_NOT_INSTALLED) + status = "Plugin downloaded."; + else + // Failed, get back to plugin status page + status = "Plugin download failed."; + } + if (plugin_status == HPLIP_PLUGIN_OUTDATED || + !strcmp(action, "install-plugin-yes") || + !strcmp(action, "license-accepted")) + { + // Install the plugin + // If failed, get back to plugin status page + status = "Plugin installation failed."; + // Plugin installation only works if we are running as root + if (!getuid()) + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Installing the proprietary plugin ..."); + if (!plugin_dir) + plugin_dir = hplip_get_uncompress_dir(system, 0); + if (plugin_dir) + { + if (hplip_install_plugin(system, plugin_dir)) + { + // Succeeded, update plugin status and get back to plugin + // status page + plugin_status = HPLIP_PLUGIN_INSTALLED; + status = "Plugin installed."; + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Plugin installed."); + } + else + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Plugin installation failed."); + } + else + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not find/the directory with the downloaded plugin."); + } + } + } +#if SNAP + else if (!strcmp(action, "remove-plugin")) + { + // Only set status to trigger the "Are you sure?" page + status = "Removing plugin."; + } + else if (!strcmp(action, "remove-plugin-yes")) + { + // Remove the plugin + // If failed, get back to plugin status page + status = "Plugin removal failed."; + // Plugin installation only works if we are running as root + if (!getuid()) + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Removing the proprietary plugin ..."); + if (!plugin_dir) + plugin_dir = hplip_get_uncompress_dir(system, 0); + if (plugin_dir) + { + if (hplip_remove_plugin(system, plugin_dir)) + { + // Succeeded, update plugin status and get back to plugin + // status page + plugin_status = HPLIP_PLUGIN_NOT_INSTALLED; + status = "Plugin removed."; + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Plugin removed."); + } + else + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Plugin removal failed."); + } + else + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Could not find/the directory with the installed plugin."); + } + } +#endif + else if (!strcmp(action, "license-declined")) + { + // License declined + // Remove downloaded and uncompressed plugin (plugin_tmp) + if (!plugin_dir) + plugin_dir = hplip_get_uncompress_dir(system, 0); + if (plugin_dir) + { + if (hplip_remove_uncompress_dir(system, "plugin_tmp") == 0) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to remove plugin directory %s/plugin_tmp", + plugin_dir); + } + } + // Set status to get back onto plugin status page + status = "License declined, plugin not installed."; + } + else if (!strcmp(action, "install-cancel")) + { + // "Are you sure?" on re-intall canceled + // Only set status to get back onto plugin status page + status = "Plugin not re-installed."; + } +#if SNAP + else if (!strcmp(action, "remove-cancel")) + { + // "Are you sure?" on remove canceled + // Only set status to get back onto plugin status page + status = "Plugin not removed."; + } +#endif // SNAP + else + status = "Unknown action."; + + cupsFreeOptions(num_form, form); + } + + // Find license file + buf[0] = '\0'; + if (status && strcasestr(status, "downloaded") && plugin_dir) + // Load license text from downloaded plugin + snprintf(buf, sizeof(buf), "%s/plugin_tmp/license.txt", plugin_dir); + else if (plugin_status != HPLIP_PLUGIN_NOT_INSTALLED) + { + // Load license text from installed plugin +#if SNAP + if (!plugin_dir) + plugin_dir = hplip_get_uncompress_dir(system, 0); + snprintf(buf, sizeof(buf), "%s/plugin/license.txt", plugin_dir); +#else + char *hplip_home = NULL; + + snprintf(buf, sizeof(buf), "%s/%s", HPLIP_CONF_DIR, "hplip.conf"); + if ((fp = fopen(buf, "r")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to open HPLIP configuration file %s", buf); + buf[0] = '\0'; + } + else + { + hplip_home = get_config_value(fp, "dirs", "home"); + fclose(fp); + if (hplip_home) + { + snprintf(buf, sizeof(buf), "%s/data/plugins/license.txt", hplip_home); + free(hplip_home); + } + else + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to locate HPLIP data directory via the config file %s, cannot load license text", + buf); + buf[0] = '\0'; + } + } +#endif // SNAP + } + + // Load license text + if (buf[0]) + { + // Open license file + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Loading license text from %s", buf); + if ((fp = fopen(buf, "r")) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to open plugin license file %s: %s", + buf, strerror(errno)); + } + else + { + // Load complete file into a buffer + fseek(fp, 0L, SEEK_END); + size_needed = ftell(fp); + licensetext = (char *)calloc(size_needed + 1, sizeof(char)); + rewind(fp); + if (fread(licensetext, 1, size_needed, fp) != size_needed) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to read plugin license file %s: %s", + buf, strerror(errno)); + free(licensetext); + licensetext = strdup("Unable to load license file."); + papplClientRespondRedirect(client, HTTP_STATUS_FOUND, "/plugin"); + } + fclose(fp); + } + } + + // Output web interface page + if (!papplClientRespond(client, HTTP_STATUS_OK, NULL, "text/html", 0, 0)) + goto clean_up; + papplClientHTMLHeader(client, "Proprietary Plugin for HPLIP", 0); + if (papplSystemGetVersions(system, 1, &version) > 0) + papplClientHTMLPrintf(client, + "
\n" + "
\n" + "
\n" + " Version %s\n" + "
\n" + "
\n" + "
\n", version.sversion); + papplClientHTMLPuts(client, "
\n"); + + papplClientHTMLPrintf(client, + "
\n" + "
\n" + "

Proprietary Plugin for HPLIP

\n"); + + if (status) + papplClientHTMLPrintf(client, "
%s
\n", status); + + if (status && strcasestr(status, "downloaded")) + { + // Show license text and buttons to accept and to reject installation + // plugin_dir /plugin_tmp/license.txt + + // Display license text + papplClientHTMLPuts(client, + "

You are about to install the proprietary plugin from HP. You have to agree with the following license to use it:\n"); + papplClientHTMLPrintf(client, + "

%s
\n", + licensetext); + + uri = papplClientGetURI(client); + + papplClientHTMLStartForm(client, uri, false); + papplClientHTMLPuts(client, + " \n" + " \n"); + + papplClientHTMLPuts(client, " \n" + " \n" + "
"); + papplClientHTMLPuts(client, " "); + papplClientHTMLPuts(client, + "
\n" + " \n"); + } + else if (status && strcasestr(status, "installing")) + { + // Ask the user whether he is sure to re-install the plugin + papplClientHTMLPuts(client, + "

You are about to re-install the already installed plugin from HP. This is only needed if you are facing problems with the plugin.

\n"); + papplClientHTMLPuts(client, + "

Are you sure?

\n"); + + uri = papplClientGetURI(client); + + papplClientHTMLStartForm(client, uri, false); + papplClientHTMLPuts(client, + " \n" + " \n"); + + papplClientHTMLPuts(client, " \n" + " \n" + "
"); + papplClientHTMLPuts(client, " "); + papplClientHTMLPuts(client, + "
\n" + " \n"); + } +#if SNAP + else if (status && strcasestr(status, "removing")) + { + // Ask the user whether he is sure to remove the plugin + papplClientHTMLPuts(client, + "

You are about to remove the installed plugin from HP. This is only needed if you are facing problems with the plugin, and if you have devices which need the plugin, they will stop working.

\n"); + papplClientHTMLPuts(client, + "

Are you sure?

\n"); + + uri = papplClientGetURI(client); + + papplClientHTMLStartForm(client, uri, false); + papplClientHTMLPuts(client, + " \n" + " \n"); + + papplClientHTMLPuts(client, " \n" + " \n" + "
"); + papplClientHTMLPuts(client, " "); + papplClientHTMLPuts(client, + "
\n" + " \n"); + } +#endif // SNAP + else + { + // Show plugin status and buttons to install, re-install, uninstall plugin + + papplClientHTMLPuts(client, + "

Some devices cannot be supported by HP's free-(open-source) software driver suite HPLIP (HP Linux Imaging and Printing) but need driver extensions or firmware files which are proprietary software (binary-only, no source code published) which cannot be distributed in this Printer Application or in Linux distributions. Therefore all these pieces of proprietary software are packaged together by HP to form a single plugin package "); + if (getuid()) + papplClientHTMLPuts(client, + "which can be installed with HPLIP.

\n"); + else + papplClientHTMLPuts(client, + "which can be installed here.

\n"); + papplClientHTMLPuts(client, + "

In most cases, for ~95% of the supported printers you do not need to install it. Only if the printer model you have selected on the \"Add Printer\" page is marked with \"requires proprietary plugin\" or you find this remark in the print queue entry once you have set up your printer, you need to install this plugin.

\n"); +#if SNAP + papplClientHTMLPuts(client, + "

With each new release of HPLIP by HP also a new version of the plugin is issued. You do not need to do any manual updates, this Snap gets auto-updated and if the update contains a new version of HPLIP, also the plugin (if it is installed) gets updated.

\n"); +#else + papplClientHTMLPuts(client, + "

With each new release of HPLIP by HP also a new version of the plugin is issued. You do not need to do any manual updates, this Printer Application automatically updates the plugin (if it is installed) when a new version of HPLIP gets installed on the system.

\n"); +#endif // SNAP + papplClientHTMLPuts(client, + "

Plugin installation status

\n"); + papplClientHTMLPrintf(client, + "

%s

\n", + plugin_status == HPLIP_PLUGIN_NOT_INSTALLED ? + "Plugin NOT installed" : + (plugin_status == HPLIP_PLUGIN_INSTALLED ? + "Plugin installed and up-to-date" : + (plugin_status == HPLIP_PLUGIN_OUTDATED ? + "Plugin out-of-date" : + "Plugin status unknown"))); + + if (getuid()) + { + if (plugin_status != HPLIP_PLUGIN_INSTALLED) + papplClientHTMLPuts(client, + "

To install or update the proprietary plugin please use the \"hp-plugin\" utility of HPLIP.

\n"); + } + else + { + uri = papplClientGetURI(client); + + papplClientHTMLStartForm(client, uri, false); + papplClientHTMLPuts(client, + " \n" + " \n"); + + papplClientHTMLPrintf(client, " \n" + " \n" + "
", + plugin_status == HPLIP_PLUGIN_NOT_INSTALLED ? + "Install Plugin" : + (plugin_status == HPLIP_PLUGIN_INSTALLED ? + "Re-install Plugin" : + (plugin_status == HPLIP_PLUGIN_OUTDATED ? + "Update Plugin" : + "Install/Update Plugin"))); +#if SNAP + if (plugin_status != HPLIP_PLUGIN_NOT_INSTALLED) + papplClientHTMLPuts(client, " "); +#endif // SNAP + papplClientHTMLPuts(client, + "
\n" + " \n"); + } + + if (plugin_status != HPLIP_PLUGIN_NOT_INSTALLED && licensetext) + { + papplClientHTMLPuts(client, + "

License

\n"); + papplClientHTMLPuts(client, + "

The proprietary plugin of HPLIP is released under the following user license:

\n"); + papplClientHTMLPrintf(client, + "
%s
\n", + licensetext); + } + } + + papplClientHTMLPuts(client, + "
\n" + "
\n"); + papplClientHTMLFooter(client); + + clean_up: + // Clean up + if (plugin_dir) + free(plugin_dir); + if (licensetext) + free(licensetext); +} + + +// +// 'hplip_plugin_support()' - Callback function for the system +// setup. It updates an already installed +// plugin, if HPLIP got update to a new +// upstream version, it adds a button to +// the system part of the main page of the +// web interface, to open the page to +// initially install the plugin, and it +// adds this page to the web interface. +// + +void +hplip_plugin_support(void *data) +{ + pr_printer_app_global_data_t *global_data = + (pr_printer_app_global_data_t *)data; + pappl_system_t *system = prGetSystem(global_data); + hplip_plugin_status_t plugin_status; + char *plugin_dir; + + + // Get status of installed plugin + plugin_status = hplip_plugin_status(system); + + if (plugin_status == HPLIP_PLUGIN_NOT_INSTALLED) + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Proprietary plugin is not installed."); + else if (plugin_status == HPLIP_PLUGIN_INSTALLED) + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Proprietary plugin is installed and up-to-date."); + else if (plugin_status == HPLIP_PLUGIN_OUTDATED) + { + // We need to update the plugin + + // Plugin installation only works if we are running as root + if (!getuid()) + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Updating an already installed proprietary plugin ..."); + if ((plugin_dir = hplip_download_plugin(system)) == NULL) + { + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Unable to download plugin ..."); + } + else + { + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Plugin downloaded to %s", plugin_dir); + + if (hplip_install_plugin(system, plugin_dir)) + papplLog(system, PAPPL_LOGLEVEL_DEBUG, + "Plugin installed."); + else + papplLog(system, PAPPL_LOGLEVEL_ERROR, + "Plugin installation failed."); + + free(plugin_dir); + } + } + } + + // Add web interface page to manage the plugin + papplSystemAddResourceCallback(system, "/plugin", "text/html", + (pappl_resource_cb_t)hplip_web_plugin, + global_data); + papplSystemAddLink(system, + getuid() ? "Proprietary Plugin Status" : + "Install Proprietary Plugin", + "/plugin", + PAPPL_LOPTIONS_OTHER | PAPPL_LOPTIONS_HTTPS_REQUIRED); +} + + +// +// 'hplip_printer_extra_web_if()' - Add button for plugin web +// interface page to print queue +// entries which need the plugin. +// Also call original function for +// adding the "Device Settings", +// page, needed especially for the +// PostScript printers. +// + +void +hplip_printer_extra_web_if(pappl_printer_t *printer, // I - Printer + void *data) // I - Global data +{ + pr_printer_app_global_data_t *global_data = + (pr_printer_app_global_data_t *)data; + pappl_system_t *system = prGetSystem(global_data); + pappl_pr_driver_data_t driver_data; + + + // "Device Settings" page for PPDs with "Installable Options" group or + // PostScript query code for printer settings + prSetupDeviceSettingsPage(printer, data); + + papplPrinterGetDriverData(printer, &driver_data); + if (strcasestr(driver_data.make_and_model, "proprietary plugin")) + { + // Printer needs the plugin, add a "Plugin" button to get to the + // plugin management web interface page to the printer's entry + papplPrinterAddLink(printer, "Plugin", "/plugin", + PAPPL_LOPTIONS_NAVIGATION | PAPPL_LOPTIONS_STATUS); + } +} + + +// +// 'main()' - Main entry for the hplip-printer-app. +// + +int +main(int argc, // I - Number of command-line arguments + char *argv[]) // I - Command-line arguments +{ + cups_array_t *spooling_conversions, + *stream_formats, + *driver_selection_regex_list; + + // Array of spooling conversions, most desirables first + // + // Here we prefer not converting into another format + // Keeping vector formats (like PS -> PDF) is usually more desirable + // but as many printers have buggy PS interpreters we prefer converting + // PDF to Raster and not to PS + spooling_conversions = cupsArrayNew(NULL, NULL); + cupsArrayAdd(spooling_conversions, (void *)&PR_CONVERT_PDF_TO_PS); + cupsArrayAdd(spooling_conversions, (void *)&PR_CONVERT_PDF_TO_RASTER); + cupsArrayAdd(spooling_conversions, (void *)&PR_CONVERT_PS_TO_PS); + cupsArrayAdd(spooling_conversions, (void *)&PR_CONVERT_PS_TO_RASTER); + + // Array of stream formats, most desirables first + // + // PDF comes last because it is generally not streamable. + // PostScript comes second as it is Ghostscript's streamable + // input format. + stream_formats = cupsArrayNew(NULL, NULL); + cupsArrayAdd(stream_formats, (void *)&PR_STREAM_CUPS_RASTER); + cupsArrayAdd(stream_formats, (void *)&PR_STREAM_POSTSCRIPT); + + // Configuration record of the Printer Application + pr_printer_app_config_t printer_app_config = + { + SYSTEM_NAME, // Display name for Printer Application + SYSTEM_PACKAGE_NAME, // Package/executable name + SYSTEM_VERSION_STR, // Version as a string + { + SYSTEM_VERSION_ARR_0, // Version 1st number + SYSTEM_VERSION_ARR_1, // 2nd + SYSTEM_VERSION_ARR_2, // 3rd + SYSTEM_VERSION_ARR_3 // 4th + }, + SYSTEM_WEB_IF_FOOTER, // Footer for web interface (in HTML) + // pappl-retrofit special features to be used + PR_COPTIONS_QUERY_PS_DEFAULTS | + PR_COPTIONS_NO_GENERIC_DRIVER | +#if !SNAP + PR_COPTIONS_USE_ONLY_MATCHING_NICKNAMES | +#endif // !SNAP + PR_COPTIONS_NO_PAPPL_BACKENDS | + PR_COPTIONS_CUPS_BACKENDS, + prAutoAdd, // Auto-add (driver assignment) callback + NULL, // Printer identify callback (HPLIP backend + // does not support this) + prTestPage, // Test page print callback + hplip_plugin_support, // Update installed plugin during system setup + // and add web interface button and page for + // plugin download + hplip_printer_extra_web_if, // Set up "Device Settings" printer web + // interface page and also add a link to + // the plugin web interface page to entries + // of printers which need the plugin. + spooling_conversions, // Array of data format conversion rules for + // printing in spooling mode + stream_formats, // Arrray for stream formats to be generated + // when printing in streaming mode + "", // CUPS backends to be ignored + "hp,HP,snmp,dnssd,usb", // CUPS backends to be used exclusively + // If empty all but the ignored backends are used + TESTPAGE, // Test page (printable file), used by the + // standard test print callback prTestPage() + ", +hpcups +[0-9]+\\.[0-9]+\\.[0-9]+[, ]*(.*)$|(\\W*[Pp]ost[Ss]cript).*$", + // Regular expression to separate the + // extra information after make/model in + // the PPD's *NickName. Also extracts a + // contained driver name (by using + // parentheses) + NULL + // Regular expression for the driver + // auto-selection to prioritize a driver + // when there is more than one for a + // given printer. If a regular + // expression matches on the driver + // name, the driver gets priority. If + // there is more than one matching + // driver, the driver name on which the + // earlier regular expression in the + // list matches, gets the priority. + }; + + return (prRetroFitPrinterApp(&printer_app_config, argc, argv)); +} diff --git a/hplip-printer-app.service b/hplip-printer-app.service new file mode 100644 index 0000000..af9c41c --- /dev/null +++ b/hplip-printer-app.service @@ -0,0 +1,10 @@ +[Unit] +Description=HPLIP Printer Application + +[Service] +ExecStart=hplip-printer-app server +ExecStop=hplip-printer-app shutdown +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/rockcraft.yaml b/rockcraft.yaml new file mode 100644 index 0000000..3d8e8a0 --- /dev/null +++ b/rockcraft.yaml @@ -0,0 +1,860 @@ +name: hplip-printer-app +base: ubuntu@22.04 +version: '3.22.10-8' +summary: HPLIP Printer Application +description: | + The HPLIP Printer Application is a PAPPL (Printer Application + Framework) based Printer Application to support printers using the + printer driver of HPLIP. Loading the proprietary plugin from HP is + supported, support for scanning will be added later. + +adopt-info: hplip + +# Only build on the architectures supported +platforms: + arm64: + amd64: + armhf: + +environment: + MIBDIRS: /hplip-printer-app/current/usr/share/snmp/mibs:/hplip-printer-app/current/usr/share/snmp/mibs/iana:/hplip-printer-app/current/usr/share/snmp/mibs/ietf + +services: + dbus: + command: /scripts/run-dbus.sh + override: replace + on-failure: restart + startup: enabled + + hplip-printer-app: + command: hplip-printer-app -o log-file=/hplip-printer-app.log server + override: replace + on-failure: restart + startup: enabled + after: [dbus] + +parts: + pappl: + source: https://github.com/michaelrsweet/pappl + source-type: git + source-tag: 'v1.4.6' + source-depth: 1 +# ext:updatesnap +# version-format: +# lower-than: '2' +# no-9x-revisions: true + plugin: autotools + override-build: | + set -eux + # Raise the supported number of vendor-specific options/attributes in + # PAPPL to 256, as the original 32 can be too small for some busy PPD + # files + perl -p -i -e 's/(define\s+PAPPL_MAX_VENDOR\s+)32/\1 256/' pappl/printer.h + # De-activate log-rotating. It does not work with the forked processes + # of the filters + perl -p -i -e 's/(system->logmaxsize\s+=).*/\1 0;/' pappl/system.c + # As we do not use PAPPL's own backends but the CUPS backends using the + # "cups" device scheme of pappl-retrofit, we let the manual "Network + # Printer" device on the "Add Printer" page of the web interface use a + # "cups:socket://..." URI instead of simply "socket://..." + perl -p -i -e 's/(httpAssembleURI\(.*?)"socket"(.*?\))/\1"cups:socket"\2/' pappl/system-webif.c + # PAPPL's build system does not insert the LDFLAGS when linking. + # Patching Makedefs.in to fix this + perl -p -i -e 's/^(\s*DSOFLAGS\s*=\s*\S*\s+)/\1\$\(LDFLAGS\) /' Makedefs.in + craftctl default + autotools-configure-parameters: + - --prefix=/usr + - --enable-libjpeg + - --enable-libpng + - --enable-libusb + - --with-dnssd=avahi + build-packages: + - libavahi-client-dev + - libgnutls28-dev + - libjpeg-dev + - libpam0g-dev + - libpng-dev + - libusb-1.0-0-dev + - zlib1g-dev + - perl-base + stage-packages: + # We stage libavahi-client3 already in the "cups" part, to stage + # everything Avahi-related there, to avoid any file clashes. + #- libavahi-client3 + - libpng16-16 + - libusb-1.0-0 + prime: + - -etc/fonts + - -var + - lib/*/lib*.so* + - usr/lib/lib*.so* + - usr/lib/*/lib*.so* + - -usr/include + - -usr/lib/pkgconfig + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + after: [cups] + + pappl-retrofit: + source: https://github.com/openprinting/pappl-retrofit + source-type: git + # source-tag: '1.0b2' + source-depth: 1 +# ext:updatesnap +# version-format: +# ignore: true +# format: '%V' + plugin: autotools + autotools-configure-parameters: + - --prefix=/usr + # To find the libraries built in this Snap + build-environment: + - LD_LIBRARY_PATH: "${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$CRAFT_STAGE/usr/lib" + build-packages: + - autoconf + - automake + - libtool + - autotools-dev + - pkg-config + - perl-base + stage-packages: + - libusb-1.0-0 + organize: + usr/share/legacy-printer-app/testpage.pdf: usr/share/hplip-printer-app/testpage.pdf + prime: + - lib/*/lib*.so* + - usr/lib/lib*.so* + - usr/lib/*/lib*.so* + - usr/share/hplip-printer-app/testpage.pdf + - -var + - -usr/bin/legacy-printer-app + - -usr/include + - -usr/lib/pkgconfig + - -usr/lib/legacy-printer-app + - -usr/share/legacy-printer-app + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + after: [cups, pappl, libcupsfilters, libppd] + + qpdf: + source: https://github.com/qpdf/qpdf + source-type: git + source-tag: 'v11.9.1' + source-depth: 1 +# ext:updatesnap +# version-format: +# lower-than: '12' +# no-9x-revisions: true + plugin: cmake + cmake-parameters: + - -DCMAKE_INSTALL_PREFIX=/ + - -DCMAKE_BUILD_RPATH_USE_ORIGIN=1 + - -DUSE_IMPLICIT_CRYPTO=0 + - -DREQUIRE_CRYPTO_GNUTLS=1 + - -DSHOW_FAILED_TEST_OUTPUT=1 + - -DCMAKE_BUILD_TYPE=RelWithDebInfo + - -DQTEST_COLOR=0 + build-packages: + - cmake + - g++ + - libjpeg-dev + - zlib1g-dev + - libgnutls28-dev + stage-packages: + - libjpeg-turbo8 + stage: + # The *.la file which gets installed by "make install" contains a + # wrong prefix, breaking parts of this Snap which use this library + - -usr/lib/lib*.la + prime: + - lib/*/lib*.so* + - usr/lib/lib*.so* + - usr/lib/*/lib*.so* + - -etc/fonts + - -var + - -usr/include + - -share/man + - -share/doc + - -share/lintian + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + - -usr/lib/libqpdf.a + - -usr/lib/libqpdf.la + - -usr/lib/pkgconfig + + ghostscript: + #source: https://git.ghostscript.com/ghostpdl.git + source: https://github.com/ArtifexSoftware/ghostpdl.git + source-type: git + source-tag: 'ghostpdl-10.03.0rc1_test' + source-depth: 1 +# ext:updatesnap +# version-format: +# format: "ghostpdl-%M.%m.%R" +# lower-than: '11' +# no-9x-revisions: true + plugin: autotools + # We only need Raster and PostScript output + autotools-configure-parameters: + - --prefix=/usr + - --without-x + - --disable-gtk + - --with-drivers=cups,pwgraster,ps2write + - --enable-freetype + - --without-tesseract + - --without-gpdl + - --without-pcl + - --without-xps + stage-packages: + - libpaper1 + - libfontconfig1 + - libfreetype6 + - libpng16-16 + prime: + - usr/bin/gs + - lib/*/lib*.so* + - usr/lib/*/lib*.so* + - usr/share/ghostscript + - -etc/fonts + - -var + - -usr/include + - -usr/lib/pkgconfig + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + after: [cups] + + cups: + source: https://github.com/OpenPrinting/cups + source-type: git + source-tag: 'v2.4.10' + source-depth: 1 +# ext:updatesnap +# version-format: +# lower-than: '3' +# no-9x-revisions: true + plugin: autotools + # We only need libcups (with headers, ...) and the backends + override-build: | + set -eux + # patch -p1 < $CRAFT_PROJECT_DIR/local/cups-dnssd-backend-socket-only.patch + # We use "--with-tls=gnutls" here, as current CUPS defaults to SSL here + # and this is buggy, causing a segfault when serving out a HTTPS web + # interface page. + ./configure --with-tls=gnutls + cd cups + make + cd .. + cd backend + # Have USB quirk files in user-modifiable space for debugging + perl -p -i -e 's/"CUPS_DATADIR"/"USB_QUIRK_DIR"/' usb-libusb.c + make snmp dnssd socket ipp ipps lpd usb + cd .. + cd ppdc + make ppdc + cd .. + mkdir -p $CRAFT_PART_INSTALL/usr/lib + cp cups/libcups*.a $CRAFT_PART_INSTALL/usr/lib/ + cp -P cups/libcups.so* $CRAFT_PART_INSTALL/usr/lib/ + mkdir -p $CRAFT_PART_INSTALL/usr/include/cups + cp cups/*.h $CRAFT_PART_INSTALL/usr/include/cups/ + mkdir -p $CRAFT_PART_INSTALL/usr/bin + cp cups-config $CRAFT_PART_INSTALL/usr/bin/ + mkdir -p $CRAFT_PART_INSTALL/usr/lib/hplip-printer-app/backend/ + ( cd backend; \ + cp snmp dnssd socket ipp ipps lpd usb org.cups.usb-quirks $CRAFT_PART_INSTALL/usr/lib/hplip-printer-app/backend/ \ + ) + cp conf/snmp.conf $CRAFT_PART_INSTALL/usr/lib/hplip-printer-app/backend/ + build-packages: + - patch + - gettext + - autoconf + - automake + - libtool + - autotools-dev + - pkg-config + - libavahi-client-dev + - libavahi-common-dev + - libavahi-compat-libdnssd-dev + - libdbus-1-dev + - libfontconfig1-dev + - libfreetype6-dev + - libgnutls28-dev + - libjpeg-dev + - libkrb5-dev + - libpam0g-dev + - libpaper-dev + - libpng-dev + - libusb-1.0-0-dev + - perl-base + stage-packages: + - libusb-1.0-0 + # We stage everything Avahi-related here, including avahi-utils only + # needed by hplip and do not stage anything of this in the pappl and + # hplip parts to avoid any file clashes. + - libavahi-common3 + - libavahi-client3 + - avahi-utils + prime: + - -etc/fonts + - -var + - -usr/include + - -usr/lib/pkgconfig + - -usr/share/cups + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + - lib/*/lib*.so* + - usr/lib/lib*.so* + - usr/lib/*/lib*.so* + - usr/lib/hplip-printer-app/backend/* + # Reported unused by snapcraft linter + - -usr/lib/*/libavahi-core.* + - -usr/lib/*/libbind9-9* + - -usr/lib/*/libdns-9* + - -usr/lib/*/libirs-9* + - -usr/lib/*/libisc-9* + - -usr/lib/*/libisccc-9* + - -usr/lib/*/libisccfg-9* + - -usr/lib/*/libns-9* + - -usr/lib/*/libdaemon.* + - -usr/lib/*/libdconf.* + - -usr/lib/*/libgdbm.* + - -usr/lib/*/libicuio.* + - -usr/lib/*/libicutest.* + - -usr/lib/*/libicutu.* + - -usr/lib/*/libicuuc.* + - -usr/lib/*/libicui18n.* + - -usr/lib/*/liblmdb.* + - -usr/lib/*/libmaxminddb.* + - -usr/lib/*/libuv.* + - -usr/lib/*/libxml2.* + + libcupsfilters: + source: https://github.com/OpenPrinting/libcupsfilters + source-type: git + source-tag: '2.0.0' + source-depth: 1 +# ext:updatesnap +# version-format: +# lower-than: '3' +# no-9x-revisions: true + plugin: autotools + # We only need libcupsfilters itself. so we simply do not prime the + # auxiliary files (/usr/share) + autotools-configure-parameters: + - --prefix=/usr + - --disable-avahi + - --disable-mutool + build-packages: + - gettext + - autoconf + - automake + - autotools-dev + - pkg-config + - g++ + - sharutils + - liblcms2-dev + - libpoppler-cpp-dev + - libpng-dev + - libjpeg-dev + - libtiff5-dev + - zlib1g-dev + - libfontconfig1-dev + - libdbus-1-dev + - libexif-dev + stage-packages: + - libpoppler-cpp0v5 + - libjbig0 + - liblcms2-2 + - libnspr4 + - libnss3 + - libopenjp2-7 + - libpoppler118 + - libtiff5 + - libwebp7 + - libexif12 + stage: + # The *.la file which gets installed by "make install" contains a + # wrong prefix, breaking parts of this Snap which use this library + - -usr/lib/lib*.la + prime: + - -etc + - -var + - -usr/include + - -usr/lib/pkgconfig + - usr/share/cups + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + - lib/*/lib*.so* + - usr/lib/lib*.so* + - usr/lib/*/lib*.so* + - usr/lib/*/nss + # Reported unused by snapcraft linter + - -usr/lib/*/libssl3.* + after: [cups, qpdf, ghostscript] + + libppd: + source: https://github.com/OpenPrinting/libppd + source-type: git + source-tag: '2.0.0' + source-depth: 1 +# ext:updatesnap +# version-format: +# lower-than: '3' +# no-9x-revisions: true + plugin: autotools + # We libppd and ppdc plus *.defs files (for HPLIP build to updated hpcups + # PPD files) + autotools-configure-parameters: + - --prefix=/usr + - --disable-mutool + - --disable-pdftocairo + - --disable-acroread + - --enable-ghostscript + - --enable-gs-ps2write + - --enable-ppdc-utils + - --with-pdftops-path=/hplip-printer-app/current/usr/bin/pdftops + # To find the libraries built in this Snap + build-environment: + - LD_LIBRARY_PATH: "${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$CRAFT_STAGE/usr/lib" + # We have to force-define the macros for the properties of the + # external executables here, as the tests do not work in the Snap + # build environment + - CFLAGS: "$CFLAGS -DHAVE_GHOSTSCRIPT_PS2WRITE -DHAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES -DHAVE_POPPLER_PDFTOPS_WITH_RESOLUTION" + build-packages: + - gettext + - autoconf + - automake + - autotools-dev + - pkg-config + - g++ + - sharutils + - poppler-utils + prime: + - -etc + - -var + - -usr/include + - -usr/lib/pkgconfig + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + - lib/*/lib*.so* + - usr/lib/lib*.so* + - usr/lib/*/lib*.so* + - usr/lib/*/nss + - usr/share/ppdc/* + after: [cups, ghostscript, libcupsfilters] + + pyppd: + source: https://github.com/OpenPrinting/pyppd + source-type: git + source-tag: 'release-1-1-0' + source-depth: 1 +# ext:updatesnap +# version-format: +# format: 'release-%M-%m-%R' +# lower-than: '2' +# no-9x-revisions: true + plugin: python + build-packages: + - python3.10 + stage-packages: + - python3.10 + override-prime: "" + + hplip: + # We use the Debian package source instead of the upstream source code + # of HPLIP as the Debian package has ~80 patches fixing bugs which are + # reported upstream but the patches not adopted upstream. + # This way we should have the same user experience in terms of reliability + # and quality as with the Debian package. + # Note that the repository has all patches already applied, so we do + # not need to apply them before building. + # Debian source + source: https://salsa.debian.org/printing-team/hplip.v2.git + source-type: git + source-tag: 'debian/3.22.10+dfsg0-5' + source-depth: 1 +# ext:updatesnap +# version-format: +# format: 'debian/%V' + # Upstream source + #source: https://sourceforge.net/projects/hplip/files/hplip/3.22.10/hplip-3.22.10.tar.gz + plugin: autotools + build-environment: + - PYTHON: python3 + - LD_LIBRARY_PATH: $CRAFT_STAGE/usr/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} + - CUPS_DATADIR: $CRAFT_STAGE/usr/share/cups + - PPDC_DATADIR: $CRAFT_STAGE/usr/share/ppdc + # Disable ImageProcesssor (for Debian source) + - IMAGEPROC: disable + # ImageProcessor oly on amd64 (for upstream source) + #- on amd64: + # - IMAGEPROC: enable + #- else: + # - IMAGEPROC: disable + - on arm64: + - BUILDARCH: aarch64-unknown-linux-gnu + - else: + - BUILDARCH: $CRAFT_ARCH_TRIPLET + # Paremeter list here only needed for upstream source, but no need to + # comment out for Debian source + autotools-configure-parameters: + - --build=$BUILDARCH + - --prefix=/usr + - --disable-foomatic-rip-hplip-install + - --without-docdir + - --without-htmldir + - --with-hpppddir=/usr/share/ppd/hplip/HP + - --without-drvdir + - --without-icondir + - --enable-hpcups-install + - --disable-cups-drv-install + - --enable-cups-ppd-install + - --disable-hpijs-install + - --disable-foomatic-drv-install + - --disable-foomatic-ppd-install + - --$IMAGEPROC-imageProcessor-build + - --enable-network-build + - --disable-class-driver + - --disable-scan-build + - --disable-gui-build + - --disable-fax-build + - --disable-qt3 + - --disable-qt4 + - --disable-qt5 + - --disable-policykit + # We need the PostScript and hpcups PPDs, the hpps and hpcups filters, + # the hp backend, and the hp-probe utility + override-build: | + set -eux + # Correct hard-coded /etc/hp/ path in Makefile + perl -p -i -e 's:/etc/hp:/hplip-printer-app/current/etc/hp:' Makefile* + # Remove hard-coded linking of binary-only libImageProcessor + # in Makefile for non-amd64 architectures (for upstream source) + #if echo $CRAFT_ARCH_TRIPLET | grep -qv x86_64; then + # perl -p -i -e 's: -lImageProcessor::' Makefile* + #fi + # Do the "./configure" (for Debian source) + sh debian/autogen.sh + ./configure \ + --build=$BUILDARCH \ + --prefix=/usr \ + --disable-foomatic-rip-hplip-install \ + --without-docdir \ + --without-htmldir \ + --with-hpppddir=/usr/share/ppd/hplip/HP \ + --without-drvdir \ + --without-icondir \ + --enable-hpcups-install \ + --disable-cups-drv-install \ + --enable-cups-ppd-install \ + --disable-hpijs-install \ + --disable-foomatic-drv-install \ + --disable-foomatic-ppd-install \ + --$IMAGEPROC-imageProcessor-build \ + --enable-network-build \ + --disable-class-driver \ + --disable-scan-build \ + --disable-gui-build \ + --disable-fax-build \ + --disable-qt3 \ + --disable-qt4 \ + --disable-qt5 \ + --disable-policykit + # Following step needed because of the Debian patches (for Debian source) + # Compress various files before building, they are needed for the build, + # and were compressed in the non-repacked upstream tarballs + find . -name '*.ppd' | xargs gzip -f + find data/ -regextype posix-extended -regex '.*\.(ldl|pcl|ps|pdf)' | xargs gzip -f + # Following step needed because of the Debian patches (for Debian source) + # Rebuild the .drv.in files from drv.in.template + python3 ./dat2drv.py + # Correct hard-coded paths in C/C++ source code + # The /etc/... and /usr/... paths in these files do not need to get + # corrected, only the /var/... ones + # perl -p -i -e 's:\"/var/lib/hp:\"/var/hplip-printer-app/common/var/:' common/utils.[ch] + # Set path for dynamic link libraries of the proprietary plugin + # We have to apply this patch here as patches on the C/C++ code need + # to get applied before compiling + # patch -p0 < $CRAFT_PROJECT_DIR/local/hplip-plugin-library-load-path.patch + # Do the "./configure; make; make install" (for upstream source) + # craftctl default + # Do the "make; make install" (for Debian source) + make + make DESTDIR=$CRAFT_PART_INSTALL install + # Correct hard-coded paths in hplip.conf + ( cd $CRAFT_PART_INSTALL/hplip-printer-app/current/etc/hp; \ + perl -p -i -e 's:/var/:/var/hplip-printer-app/common/var/:' hplip.conf; \ + perl -p -i -e 's:/usr/share/:/hplip-printer-app/current/usr/share/:' hplip.conf; \ + perl -p -i -e 's:/usr/share/ppd/hplip:/usr/share/ppd:' hplip.conf; \ + perl -p -i -e 's:/usr/share/ppd/HP:/usr/share/ppd:' hplip.conf; \ + perl -p -i -e 's:/usr/lib/cups/:/hplip-printer-app/current/usr/lib/hplip-printer-app/:' hplip.conf; \ + ) + # Correct hard-coded /var and /etc paths in the utilities written in + # Python + ( cd $CRAFT_PART_INSTALL/usr/share/hplip/ + perl -p -i -e 's:/var/(\S+)/hp:/var/hplip-printer-app/common/var/\1/hp:' *.py */*.py */*/*.py + perl -p -i -e 's:/var/lib/hp:/var:' *.py */*.py */*/*.py + perl -p -i -e 's:/etc/hp:/hplip-printer-app/current/etc/hp:' *.py */*.py */*/*.py + # Set path for firmware files of the proprietary plugin + # We have to apply this patch here so that the global corrections done + # right above do not mess it up + # patch -p0 < $CRAFT_PROJECT_DIR/local/hplip-plugin-firmware-load-path.patch + ) + # Correct Python shebang in the utilities + ( cd $CRAFT_PART_INSTALL; \ + for file in usr/bin/hp-*; do \ + perl -p -i -e 's:^\s*\#\!\s*/usr/bin/env\s+python.*:#!/hplip-printer-app/current/usr/bin/python3:' `readlink -f $file`; \ + done; \ + ) + # "make install" install Python extension modules to the wrong place + # (for Debian source) + rm $CRAFT_PART_INSTALL/usr/lib/python*/site-packages/*.la + mkdir -p $CRAFT_PART_INSTALL/usr/lib/python3/dist-packages + mv $CRAFT_PART_INSTALL/usr/lib/python*/site-packages/*.so $CRAFT_PART_INSTALL/usr/lib/python3/dist-packages + # "make install" misses to install the PostScript PPD files, do it now + # (for Debian source) + cp prnt/ps/*.ppd.gz $CRAFT_PART_INSTALL/usr/share/ppd/hplip/HP/ + # Handle the PPD files: Unzip, remove "(recommended)" (we have only + # HPLIP here, no other driver), compress into self-extracting archive + ( cd $CRAFT_PART_INSTALL/usr/share/ppd/hplip/HP/; \ + find . -name '*.gz' | xargs gunzip -f; \ + for file in `find . -name '*.ppd'`; do \ + perl -p -i -e 's/(\*NickName:.*\S+)\s*\(recommended\)/\1/' $file; \ + done; \ + PYTHONPATH=$CRAFT_STAGE/lib/python3.10/site-packages pyppd -v -o hplip-ppds .; \ + mv hplip-ppds ../..; \ + cd ../..; \ + rm -rf hplip; \ + ) + # Link Python 3 to the "python" executable name (no older Python in this + # Snap and everything needs Python 3) + ln -sf python3 $CRAFT_PART_INSTALL/usr/bin/python + # Install the binary-only libImageProcessor only if we build the Snap for + # amd64 (x86_64) (for upstream source) + #if echo $CRAFT_ARCH_TRIPLET | grep -q x86_64; then + # cp prnt/hpcups/libImageProcessor-x86_64.so $CRAFT_PART_INSTALL/usr/lib/libImageProcessor.so + #fi + # Install the public key for verifying the signature of the proprietary + # plugin (for Debian source) + cp debian/upstream/signing-key.asc $CRAFT_PART_INSTALL/usr/share/hplip/ + build-packages: + - ubuntu-dev-tools + - dpkg-dev + - fakeroot + - automake + - gawk + - python3-dev + - libpython3-dev + - fdupes + - libavahi-client-dev + - libavahi-core-dev + - libdbus-1-dev + - libjpeg-dev + - libsnmp-dev + - libssl-dev + - libtool + - libudev-dev + - libusb-1.0-0-dev + - perl-base + - python3 + - xz-utils + - curl + - python3-dev + stage-packages: + - python3 + - python3-minimal + - python3.10 + - python3.10-minimal + - python3-dbus + - python3-distro + - python3-gi + - python3-pexpect + - python3-pil + - python3-reportlab + - libpython3.10 + - libpython3.10-stdlib + - wget + - xz-utils + - python3-dev + # We stage avahi-utils already in the "cups" part, to stage + # everything Avahi-related there, to avoid any file clashes. + #- avahi-utils + - libsnmp40 + - libsnmp-base + - pyppd + organize: + usr/lib/cups/filter/hpcups: usr/lib/hplip-printer-app/filter/hpcups + usr/lib/cups/filter/hpps: usr/lib/hplip-printer-app/filter/hpps + usr/lib/cups/backend/hp: usr/lib/hplip-printer-app/backend/hp + prime: + # - etc/hp + - -var + - usr/bin + - -usr/bin/pdb* + - -usr/bin/py*versions + - lib/*/lib*.so* + - usr/lib/lib*.so* + - usr/lib/*/lib*.so* + - usr/lib/python* + - -usr/include + - -usr/lib/pkgconfig + - -usr/share/fonts + - -usr/share/man + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/lintian + - usr/share/hplip + - usr/share/ppd + - usr/share/snmp + - usr/lib/hplip-printer-app + - -usr/lib/cups + # Reported unused by snapcraft linter + - -usr/lib/libhpip.* + - -usr/lib/*/libavahi-core.* + - -usr/lib/*/libgdbm.* + - -usr/lib/*/libgdbm_compat.* + - -usr/lib/*/libnetsnmpagent.* + - -usr/lib/*/libnetsnmpmibs.* + - -usr/lib/*/libpci* + - -usr/lib/*/libsensors.* + - -usr/lib/*/libsnmp.* + after: [cups, pyppd] + + hplip-printer-app: + plugin: make + source: . + make-parameters: + - LDFLAGS="$LDFLAGS -ljpeg" + - VERSION="$VERSION" + - HPLIP_CONF_DIR=/hplip-printer-app/current/etc/hp + - HPLIP_PLUGIN_STATE_DIR=/var/hplip-printer-app/common/var + - HPLIP_PLUGIN_ALT_DIR=/var/hplip-printer-app/common + - SNAP=1 + # To find the libraries built in this Snap + build-environment: + - LD_LIBRARY_PATH: "${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$CRAFT_STAGE/usr/lib" + # To improve convenience for developers (and everyone who wants to + # build from source), we do a "make clean" before "make" here, + # because if we had done "make" off-Snap, directly in the source + # tree, and afterwards build the Snap with snapcraft, the build + # sucks in our local binary of hplip-printer-app instead of + # compiling its own one in the Snap harness with the appropriate + # libraries, ending up with the Snap containing an executable + # which does not work inside the Snap. The "make clean" removes + # any accidentally grabbed binary. + # + # We need to directly call the "make" and "make install" commands + # here as we cannot inject an environment variable into the + # default build process ("craftctl default") and we also cannot + # call "craftctl get version" in the lines of "make-parameters:" + # or "build-environment:". This way we get the version number of + # our Snap (which is extracted from the HPLIP upstream source) + # inyo the ghostscript-printer-app executable. + override-build: | + set -eux + make clean + VERSION="`craftctl get version`" + make -j"8" LDFLAGS="$LDFLAGS -ljpeg" SNAP=1 VERSION="$VERSION" + make -j"8" install LDFLAGS="$LDFLAGS -ljpeg" SNAP=1 VERSION="$VERSION" DESTDIR="$CRAFT_PART_INSTALL" + #craftctl default + build-packages: + - libusb-1.0-0-dev + - libcurl4-gnutls-dev + - libssl-dev + - libjpeg-dev + stage-packages: + - libusb-1.0-0 + - libjbig0 + - liblcms2-2 + - libtiff5 + - libwebp7 + - libcurl3-gnutls + - libssl3 + - libldap-2.5-0 + - libnghttp2-14 + - librtmp1 + - libsasl2-2 + - libssh-4 + - gpg + - gpg-agent + stage: + - -usr/lib/hplip-printer-app + prime: + - usr/bin/hplip-printer-app + - lib/*/lib*.so* + - usr/lib/*/lib*.so* + - usr/share/hplip-printer-app + - usr/bin/gpg* + - usr/lib/gnupg* + - usr/lib/sasl* + - usr/lib/python* + - -var + - -usr/var + - -usr/share/man + # Reported unused by snapcraft linter + - -usr/lib/*/libgssapi.* + after: [pappl-retrofit, pappl, cups, libcupsfilters, libppd, hplip] + + avahi-daemon: + plugin: nil + overlay-packages: + - avahi-daemon + - avahi-utils + - libnss-mdns + - mdns-scan + - dbus + - python3 + + scripts: + plugin: dump + source: . + organize: + # "HP" discovery-only CUPS backendto discover + # network printers using the hp-probe utility, as + # HPLIP's "hp" backend only discovers USB printers + HP: usr/lib/hplip-printer-app/backend/HP + stage-packages: + - udev + prime: + - -etc + - bin + - -bin/systemd-hwdb + - -lib + - scripts/ + - usr/lib/hplip-printer-app/backend/ + - -usr/lib/tmpfiles.d + after: [hplip-printer-app] + + dbus-scripts: + plugin: dump + source: scripts/ + organize: + run-dbus.sh: /scripts/run-dbus.sh + override-prime: | + set -eux + craftctl default + # Ensure the run-dbus.sh script has executable permissions + if [ -f "$CRAFT_PRIME/scripts/run-dbus.sh" ]; then + chmod +x "$CRAFT_PRIME/scripts/run-dbus.sh" + fi diff --git a/scripts/run-dbus.sh b/scripts/run-dbus.sh new file mode 100644 index 0000000..23f91ea --- /dev/null +++ b/scripts/run-dbus.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -eux + +echo "Creating system users" + +# Create system users with system accounts and no home directories, using nologin shell +useradd --system --no-create-home --shell /usr/sbin/nologin systemd-resolve || true +useradd --system --no-create-home --shell /usr/sbin/nologin systemd-network || true + +echo "Creating directories" + +# Create the /run/dbus directory if it doesn't exist, set permissions, and ownership +mkdir -p /run/dbus +chmod 755 /run/dbus +chown root:root /run/dbus + +echo "Starting dbus" + +# Start the dbus daemon in the foreground +# dbus-daemon --system --nofork --nopidfile & + +# Check the status of the dbus service +service dbus start +service dbus status || true + +echo "Starting avahi-daemon" + +# Start the avahi-daemon in the background without dropping root privileges +avahi-daemon --daemonize --no-drop-root + +# Keep the script running to avoid container exit +tail -f /dev/null \ No newline at end of file diff --git a/testpage.ps b/testpage.ps new file mode 100644 index 0000000000000000000000000000000000000000..127af3f1164f4c6f047ccd268467e487413b7700 GIT binary patch literal 17190 zcmds8X>Z#|n*Q7d_CM5S;f37YvMwL(0Kp=5(uvVd+OX2wGr)YIq>{`Tiqw*nk8R|? z@AJG>EEW&RO4=FhZaOou$+xQB`z-GN>-D(%dX`LdcXZg_`a-iIR=qIG);jYKD0k#f&-G_-fB!sL$1`AYo;5=Azobv3!S}G1N?Xu z#EUh4z0!9&GLPOQ)AbVkeG}xtOLY$W_Bn{F>Y|@!&zMH9enk;3PYv}8Enue>~J7J!rvg3iAe37gk z({OQ{tIqU54g15snmmGoQKWtemaCP9@?q~fSpT#;><Ck+^(vTxdizw}4G;S;(XrM-&OC&`UR+&Xy!|+S z`$CbBWs*V)Dva^VGRPr8NOP|@e*2eCuRnfLe|-1dcl%&ja1U#pLM$n*A3BifhdhY$ z!^6WpfB>cw2TOf`w_&bT8qC6Vrc$*~6IJZh-BSt(C&>!Pq{(`5s|NkP%5H<1&bX1H zp&a%H{0~2z{iOcDGcFoaw?UU+U_h0ctyh3LJyswJcfVtu~~@>{c3v^C%}#fJq9)z#BLDndGd zx>#!yJ(Ob@Z$?gM5PSM6NT&c7L+0sW9K&1Sp%J$+1Z#QJ5bqcqLZj%|Q+{wJN_BzO zw48|Z-^$%^egFluG z@uV6B6CD|52tg!i9NZQD7{^f1qdxp7axu67y#I@@KzQC6^=Ym8VR}o*EQ|}tzxt4P zZXrF=5iUc95G9CE6pIYU<6p(S(j-TAAVl2Aj~0%vtp*+!hy6OAgX`o0)l~wlY$JGe z5hWaC0O8J(`?#=@)I-SbV$k6+*==%fxQHLz@rx7*2?OD0HG!j7Gdbe4HIvu`232g>F57CuK>di*(tf)-IK1 zwv7Slg+^gUm#G6eXQuCvU!n@kl4vc-p@ge24)ZCBt0HQ_ zdxa}HZV6YDN%8>dsqhn91z`s|xmcdEC-ai8FS4afAHvJ)%8vt^K?5CFsR z7YQx7o*jiOq{{N&v>M zTJbr_a?zy>0hF|Odl$%rzbw5=K4PhG@OR^6z)Qmva#hgG))P818U!90uvhZ40qn}p z1TD6DuZ69JT==i$mfg#2Wx>o|6rkD3fYRwhQWO?+KF(NH2<4P)L!HzLmP!c|g-5Ec zQ8UBiV3G+1Ai;n4O5sK45lYEG)qB{o%ztN=J;*YwL!KNi)^I?`ON`eH{~Nslam?Vf z%@KN1?Tqot_?8Sm5k?+Ud?j!BlVwdrmj252Y$_pMRT?fvr>qRB{DFv@V)9ut5XM-yk**`1W-5r#rxOj= zGc=y8HZftj{P30;4s3OJczh&InF^n- z-3`a=ctp+-7k;a7fA$FJELdrHn3y~dxt<(BH!fkpmyHDg13Ce$YGe zWl*PeaJ(C>9#UuOY{N-EZ9D1YOAbQq0aq_sJ$NEXkJT(2(>!7J1snU^%Dq%9r&kl3 zI{X>UGO^d!EfbkNY?`WNwW?3O+DWTM(&ibS3SEUO`AW57uK5gF)g-({uN6EA)T;xD(ewy(T!+&Fe0W0@%Om9BB(Gx<&G zQeAP3=k&C!beoWnYvQ2D`xe?=F)dLx<4o&Vk|WOMsc}2|7GJf^X;tMm=WWpKs^%b@ zW@nYh8(jWZsBn{oY%^_V;FmaOGO(e*wL{xgxD?n|dugYEv>G;Wg6R|;2~2{JqDE{J zbx{N5w%cGbV}g1qCL9r;RB{76b82qNwK1D}=<-OxCY=*+Qsvb{e&)+u89TZ9-=w`4 zoU!5YPDv}^3fF=DjT~tzQ$tGGd}XiPwq~UVDI+}eY59wAfYHk=GV~g~MnVW*Ea~_I zn!jX(dGS4(kw<9%pALaS#YLlzD3(0hB{t@uob=aJI7m8JWD7!4TNlW<`rG94b#iSv zV^XHh#X+Uv&X`mTA2E_J?Po!lJ=)F(9~uM4?a+U%LKA+=-Zxcf&O^39zw@Dq$5!a` zDs(&EMlK1<6F(0|UBwxJ@VTKcrF?f2JmIZykEE#}n$i&a;H)pFmc0QhMr2 zb;JPF@6^x(iM?-c1lV=A#m?xUa9Lz#%+HOrIHkcjY`J>E030034Z{oBuTZ*@5u+2Q z&P@w&M17qFIMr6D_=Lu;v0hq&$gg1|TZiZLC2iw#F)llO5SbTb8moa%a>Fh0WAF)l zAoV&y1(k}Ysm(6^=_P{NxMh54_b5rm2uDhImiin)b<0;hb!@?l?z$=VO9*DCPWo(v1N(H-lFeL$Hjp<_D&`Y z|1G50&7lEg2C3?htm>e(jYQL5Fw34G5$tZ#C}FNfqCbZOHZ5~mg(j9vX7Rc{nuQ_o zf&%HdMz-h;XF2V#)C_7h@FoXvgauDo zfeC!SR3s~_LMH(I8?N)9x8;B zxyXD&G90>kp{nzX=S#7UqpdIrx2~`mP0P_V1U)s2-HuwBYQ7}-W z<1=?}$vKWU_HL7m3o%=YlU%KF!%Y|@G0vI0mvw)TEPfxN(Y4$LA8zye!)JI0_J%XLfC0grOMj^rBC=1)qAe-uPS zz&6HivJeO3XXc*xwn*HQ_wyxa1B{JDpf^U{uQS^V0vrnpvSk~4_mY!x`0^6ao{@)uqeCDmeFgex)h1$2#4({u>j zpafkiYLL&m50d96>OuXxdIYr{x9p`lP%3?Z<scSr_GQ zm$nP0Jgacab2i9U`8T&bA$gBCNw;!W%v-27Dy+CI{BHeLi5%_~TPDa%x5|Z0 zo@-k`8dT^SogHFC@A>9^|MgDvwQSL#v+AB_tHM8DvW2=zIjzQ5Tqx-6_djyQ(-U`l zR_-nKJ|3!NsrFj4#MFAmbhaUp`2UfgqbIdta!AB}pZ(b#_ zl63Dzf!(zoeSAc~BR%X(Bs%JEoeEv5V`xfo`Ce9eLF~ppV{rJ}L1o!NQCpbLL!X&E z`_XATc`I|IN1g>&{3z6vJY#%Yi{|J@^QFeF`GneD-`P$7tsN~k7kH?-BjmIKx-;&5 zG+4RGpw+P zNs)3lxLd08PVoV=`+C|n-AIvir+Edz^gk=3QPbkbE$A!*#&T1k?lyO%&ua|W5~5_o zHo9sTupN2k$}qLrP&dQ(QTU96_Yx{!aKFF4y1aIuaP!M^e74RHQVJf_#L3n`D%a}J zKISJZ97JGK!-+caWw%4BD!Iy6(&eQ>Wq Date: Mon, 12 Aug 2024 18:33:22 +0530 Subject: [PATCH 02/13] Implemented the latest workflow from ubuntu/desktop-snaps, which incorporates an automated rock versioning system. - In the workflow file .github/workflows/auto-update.yml, we add the parameter "rock-version-schema: '^debian/(\d+\.\d+\.\d+)' to the call of the GitHub action. This instructs the action to perform versioning automation and specifies the format of the upstream release tags and the upstream version number in them. - We add "adopt-info: hplip" even with all metadata fields in rockcraft.yaml. This tells the GitHub action which part is the supplier of the upstream version number. Add GitHub Action to Push Image to Docker Registry - Added registry-actions.yml to automate pushing Docker images to the Docker registry. - Utilized craft-action/rockcraft-pack from Canonical to pack the rock. - Integrated docker/login-action for seamless authentication to Docker Hub within the GitHub Actions workflow. - Configured the workflow to pack rock as a Docker image using Skopeo. Added patch-files in repository --- .github/workflows/auto-update.yml | 20 +++ .github/workflows/registry-actions.yml | 117 ++++++++++++++++++ .gitignore | 3 +- .../cups-dnssd-backend-socket-only.patch | 36 ++++++ .../hplip-plugin-firmware-load-path.patch | 11 ++ .../hplip-plugin-library-load-path.patch | 11 ++ rockcraft.yaml | 8 +- 7 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/auto-update.yml create mode 100644 .github/workflows/registry-actions.yml create mode 100644 patch-files/cups-dnssd-backend-socket-only.patch create mode 100644 patch-files/hplip-plugin-firmware-load-path.patch create mode 100644 patch-files/hplip-plugin-library-load-path.patch diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml new file mode 100644 index 0000000..f85effe --- /dev/null +++ b/.github/workflows/auto-update.yml @@ -0,0 +1,20 @@ +name: Push new tag update to stable branch + +on: + schedule: + # Daily for now + - cron: "9 7 * * *" + workflow_dispatch: + +jobs: + update-snapcraft-yaml: + runs-on: ubuntu-latest + steps: + - name: Checkout this repo + uses: actions/checkout@v3 + - name: Run desktop-snaps action + uses: ubuntu/desktop-snaps@stable + with: + token: ${{ secrets.GITHUB_TOKEN }} + repo: ${{ github.repository }} + rock-version-schema: '^debian/(\d+\.\d+\.\d+)' diff --git a/.github/workflows/registry-actions.yml b/.github/workflows/registry-actions.yml new file mode 100644 index 0000000..0467f15 --- /dev/null +++ b/.github/workflows/registry-actions.yml @@ -0,0 +1,117 @@ +name: Pack and Publish OCI Image to Docker Registry + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + workflow_choice: + description: "Choose Release Channel" + required: true + default: "edge" + type: choice + options: + - edge + - stable + +jobs: + build-rock: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Pack with Rockcraft + uses: canonical/craft-actions/rockcraft-pack@main + id: rockcraft + + - name: Upload Rock Artifact + uses: actions/upload-artifact@v4 + with: + name: cups-rock + path: ${{ steps.rockcraft.outputs.rock }} + + publish-rock-edge: + needs: build-rock + if: github.ref_name == 'main' && (github.event.inputs.workflow_choice == 'edge' || github.event_name == 'push') + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download Rock Artifact + uses: actions/download-artifact@v4 + with: + name: cups-rock + + - name: Install Dependencies + run: | + sudo snap install rockcraft --classic + sudo snap install docker + sudo snap install yq + + - name: Ensure Docker Daemon is Running + run: | + sudo systemctl start docker + sudo systemctl enable docker + sudo systemctl is-active --quiet docker || sudo systemctl start docker + + - name: Log in to Docker Hub + uses: docker/login-action@v3.2.0 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Docker Image + env: + USERNAME: ${{ secrets.DOCKER_USERNAME }} + run: | + IMAGE="$(yq '.name' rockcraft.yaml)" + VERSION="$(yq '.version' rockcraft.yaml)" + ROCK="$(ls *.rock | tail -n 1)" + sudo rockcraft.skopeo --insecure-policy copy oci-archive:"${ROCK}" docker-daemon:"${USERNAME}/${IMAGE}:${VERSION}-edge" + docker push ${USERNAME}/${IMAGE}:${VERSION}-edge + docker tag ${USERNAME}/${IMAGE}:${VERSION}-edge ${USERNAME}/${IMAGE}:latest + docker push ${USERNAME}/${IMAGE}:latest + + publish-rock-stable: + needs: build-rock + if: github.ref_name == 'main' && github.event.inputs.workflow_choice == 'stable' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download Rock Artifact + uses: actions/download-artifact@v4 + with: + name: cups-rock + + - name: Install Dependencies + run: | + sudo snap install rockcraft --classic + sudo snap install docker + sudo snap install yq + + - name: Ensure Docker Daemon is Running + run: | + sudo systemctl start docker + sudo systemctl enable docker + sudo systemctl is-active --quiet docker || sudo systemctl start docker + + - name: Log in to Docker Hub + uses: docker/login-action@v3.2.0 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Docker Image + env: + USERNAME: ${{ secrets.DOCKER_USERNAME }} + run: | + IMAGE="$(yq '.name' rockcraft.yaml)" + VERSION="$(yq '.version' rockcraft.yaml)" + ROCK="$(ls *.rock | tail -n 1)" + sudo rockcraft.skopeo --insecure-policy copy oci-archive:"${ROCK}" docker-daemon:"${USERNAME}/${IMAGE}:${VERSION}-stable" + docker push ${USERNAME}/${IMAGE}:${VERSION}-stable diff --git a/.gitignore b/.gitignore index 013c47c..fa9a08a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.rock \ No newline at end of file +*.rock +.DS_Store \ No newline at end of file diff --git a/patch-files/cups-dnssd-backend-socket-only.patch b/patch-files/cups-dnssd-backend-socket-only.patch new file mode 100644 index 0000000..ccaa3a1 --- /dev/null +++ b/patch-files/cups-dnssd-backend-socket-only.patch @@ -0,0 +1,36 @@ +diff --git a/backend/dnssd.c b/backend/dnssd.c +index e57029178..77c7724b3 100644 +--- a/backend/dnssd.c ++++ b/backend/dnssd.c +@@ -297,8 +297,8 @@ main(int argc, /* I - Number of command-line args */ + return (0); + } + +- browsers = 6; +- avahi_service_browser_new(client, AVAHI_IF_UNSPEC, ++ browsers = /*6*/1; ++ /*avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_fax-ipp._tcp", NULL, 0, + browse_callback, devices); +@@ -313,17 +313,17 @@ main(int argc, /* I - Number of command-line args */ + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_ipps._tcp", NULL, 0, +- browse_callback, devices); ++ browse_callback, devices);*/ + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_pdl-datastream._tcp", + NULL, 0, + browse_callback, + devices); +- avahi_service_browser_new(client, AVAHI_IF_UNSPEC, ++ /*avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_printer._tcp", NULL, 0, +- browse_callback, devices); ++ browse_callback, devices);*/ + #endif /* HAVE_AVAHI */ + + /* diff --git a/patch-files/hplip-plugin-firmware-load-path.patch b/patch-files/hplip-plugin-firmware-load-path.patch new file mode 100644 index 0000000..4d2977e --- /dev/null +++ b/patch-files/hplip-plugin-firmware-load-path.patch @@ -0,0 +1,11 @@ +--- base/device.py.orig 2021-10-01 21:15:42.022561141 +0200 ++++ base/device.py 2021-10-01 21:17:40.471403504 +0200 +@@ -2622,7 +2622,7 @@ + + def downloadFirmware(self, usb_bus_id=None, usb_device_id=None): # Note: IDs not currently used + ok = False +- filename = os.path.join(prop.data_dir, "firmware", self.model.lower() + '.fw.gz') ++ filename = '/var/snap/hplip-printer-app/common/plugin/' + self.model.lower() + '.fw.gz' + log.debug(filename) + + if os.path.exists(filename): diff --git a/patch-files/hplip-plugin-library-load-path.patch b/patch-files/hplip-plugin-library-load-path.patch new file mode 100644 index 0000000..14f6b48 --- /dev/null +++ b/patch-files/hplip-plugin-library-load-path.patch @@ -0,0 +1,11 @@ +--- common/utils.c.orig 2021-10-01 16:29:26.049843045 +0200 ++++ common/utils.c 2021-10-01 16:32:57.678932412 +0200 +@@ -227,6 +227,8 @@ + return pHandler; + } + ++ snprintf(szLibraryFile, sizeof(szLibraryFile), "/var/snap/hplip-printer-app/common/plugin/%s", szPluginName); ++ + return load_library (szLibraryFile); + + } diff --git a/rockcraft.yaml b/rockcraft.yaml index 3d8e8a0..33364a4 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -248,7 +248,7 @@ parts: # We only need libcups (with headers, ...) and the backends override-build: | set -eux - # patch -p1 < $CRAFT_PROJECT_DIR/local/cups-dnssd-backend-socket-only.patch + patch -p1 < $CRAFT_PROJECT_DIR/patch-files/cups-dnssd-backend-socket-only.patch # We use "--with-tls=gnutls" here, as current CUPS defaults to SSL here # and this is buggy, causing a segfault when serving out a HTTPS web # interface page. @@ -596,7 +596,7 @@ parts: # Set path for dynamic link libraries of the proprietary plugin # We have to apply this patch here as patches on the C/C++ code need # to get applied before compiling - # patch -p0 < $CRAFT_PROJECT_DIR/local/hplip-plugin-library-load-path.patch + patch -p0 < $CRAFT_PROJECT_DIR/patch-files/hplip-plugin-library-load-path.patch # Do the "./configure; make; make install" (for upstream source) # craftctl default # Do the "make; make install" (for Debian source) @@ -619,7 +619,7 @@ parts: # Set path for firmware files of the proprietary plugin # We have to apply this patch here so that the global corrections done # right above do not mess it up - # patch -p0 < $CRAFT_PROJECT_DIR/local/hplip-plugin-firmware-load-path.patch + patch -p0 < $CRAFT_PROJECT_DIR/patch-files/hplip-plugin-firmware-load-path.patch ) # Correct Python shebang in the utilities ( cd $CRAFT_PART_INSTALL; \ @@ -824,7 +824,7 @@ parts: - libnss-mdns - mdns-scan - dbus - - python3 + - python3f scripts: plugin: dump From 4a3258fdd6077963fb43ccacdabb828cd2ff8cd3 Mon Sep 17 00:00:00 2001 From: Rudra-IITM Date: Mon, 12 Aug 2024 18:48:56 +0530 Subject: [PATCH 03/13] correct python3 name in avahi-daemon parts --- rockcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rockcraft.yaml b/rockcraft.yaml index 33364a4..b043b99 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -824,7 +824,7 @@ parts: - libnss-mdns - mdns-scan - dbus - - python3f + - python3 scripts: plugin: dump From 9e6412a6a9d8325ad6a9aaf1f15c0c910b3b27b4 Mon Sep 17 00:00:00 2001 From: Rudra-IITM Date: Thu, 15 Aug 2024 19:43:22 +0530 Subject: [PATCH 04/13] Updated documentation for hp-printer-app --- LICENSE | 202 --------------------------------------------- NOTICE | 6 -- README.md | 238 +++++++++++------------------------------------------- 3 files changed, 49 insertions(+), 397 deletions(-) delete mode 100644 LICENSE delete mode 100644 NOTICE diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d645695..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 33449ca..0000000 --- a/NOTICE +++ /dev/null @@ -1,6 +0,0 @@ -HPLIP Printer Application - -Copyright © 2020-2021 by Till Kamppeter. -Copyright © 2020 by Michael R Sweet. -Copyright © 2007-2019 by Apple Inc. -Copyright © 1997-2007 by Easy Software Products. diff --git a/README.md b/README.md index 3aa5fe6..45ab170 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ internally. Also their utilities need to be made independent of CUPS.** For PostScript printers you can also use the [PostScript Printer -Application](https://github.com/OpenPrinting/ps-printer-app), +Application]() - Link to be updated, especially if you have it already installed for some non-HP PostScript printer. @@ -102,7 +102,7 @@ first. - The information about which printer models are supported and which are their capabilities is based on the PPD files included in - HPLIP. They are packaged in the Snap as a compressed archive. + HPLIP. They are packaged in the Rock as a compressed archive. - Standard job IPP attributes are mapped to the driver's option settings best fitting to them so that users can print from any type @@ -122,10 +122,10 @@ first. option settings (the original options are still accessible via web admin interface). -- The Snap of the HPLIP Printer Application takes HPLIP's source code +- The Rock of the HPLIP Printer Application takes HPLIP's source code from Debian's packaging repository instead of directly from HP, as Debian's package has ~80 patches fixing bugs which are reported to - HP but the patch not adopted upstream. So with the Snap users should + HP but the patch not adopted upstream. So with the Rock users should get the same experience in reliability and quality as with the Debian package. @@ -133,223 +133,83 @@ first. additional page in the web interface. This adds support for some laser printers which need their firmware loaded every time they are turned on or which use certain proprietary print data formats. This - works both in the Snap and in the classic installation of the + works both in the Rock and in the classic installation of the Printer Application (must run as root, otherwise only status check of the plugin). -### To Do +## Install from Docker Hub +### Prerequisites -- Support for scanning on HP's multi-function printers. this requires - scanning support in PAPPL (which made [good progress in GSoC - 2021](https://github.com/michaelrsweet/pappl/commits/scanning)). +1. **Docker Installed**: Ensure Docker is installed on your system. You can download it from the [official Docker website](https://www.docker.com/get-started). -- PDF test page, for example generated with the bannertopdf filter. +### Step-by-Step Guide -- Human-readable strings for vendor options (Needs support by PAPPL: - [Issue #58: Localization - support](https://github.com/michaelrsweet/pappl/issues/58)) - -- Internationalization/Localization (Needs support by PAPPL: [Issue - #58: Localization - support](https://github.com/michaelrsweet/pappl/issues/58)) - -- SNMP Ink level check via ps_status() function (Needs support by PAPPL: - [Issue #83: CUPS does IPP and SNMP ink level polls via backends, - PAPPL should have functions for - this](https://github.com/michaelrsweet/pappl/issues/83)) - -- Build options for cups-filters, to build without libqpdf and/or - without libppd, the former will allow to create the Snap of this - Printer Application without downloading and building QPDF - - -## THE SNAP - -### Installing and building - -To just run and use this Printer Application, simply install it from -the Snap Store: - -``` -sudo snap install --edge hplip-printer-app -``` - -Then follow the instructions below for setting it up. - -To build the Snap by yourself, in the main directory of this -repository run - -``` -snapcraft snap -``` - -This will download all needed packages and build the HPLIP Printer -Application. Note that PAPPL (upcoming 1.0) and cups-filters (upcoming -2.0) are pulled directly from their GIT repositories, as there are no -appropriate releases yet. This can also lead to the fact that this -Printer Application will suddenly not build any more. - -To install the resulting Snap run +#### 1. Pull the hplip-printer-app docker image: +The first step is to pull the hplip-printer-app Docker image from Docker Hub. +```sh +sudo docker pull openprinting/hplip-printer-app ``` -sudo snap install --dangerous hplip-printer-app_1.0_amd64.snap -``` - -### Setting up +#### 2. Start the hplip-printer-app Container -The Printer Application will automatically be started as a server daemon. - -Enter the web interface - -``` -http://localhost:8000/ +##### Run the following Docker command to run the hplip-printer-app image: +```sh +sudo docker run --rm -d --name hplip-printer-app -p :8000 \ + openprinting/hplip-printer-app:latest ``` -Use the web interface to add a printer. Supply a name, select the -discovered printer, then select make and model. Also set the installed -accessories, loaded media and the option defaults. If the printer is a -PostScript printer, accessory configuration and option defaults can -also often get polled from the printer. - -If the entry of your printer in the web interface has the remark -"requires proprietary plugin", you need to install HP's plugin. For -this, click on the "Plugin" button in this printer entry or on the -"Install Proprietary Plugin" button under "Other Settings" on the -front page of the web interface and follow the instructions on the -screen. - -Then print PDF, PostScript, JPEG, Apple Raster, or PWG Raster files -with +## Setting Up and Running hplip-printer-app locally -``` -hplip-printer-app FILE -``` +### Prerequisites -or print with CUPS, CUPS (and also cups-browsed) discover and treat -the printers set up with this Printer Application as driverless IPP -printers (IPP Everywhere and AirPrint). +1. **Docker Installed**: Ensure Docker is installed on your system. You can download it from the [official Docker website](https://www.docker.com/get-started). -See - -``` -hplip-printer-app --help +2. **Rockcraft**: Rockcraft should be installed. You can install Rockcraft using the following command: +```sh +sudo snap install rockcraft --classic ``` -for more options. +3. **Skopeo**: Skopeo should be installed to compile `.rock` files into Docker images.
+**Note**: It comes bundled with Rockcraft. -Use the "-o log-level=debug" argument for verbose logging in your -terminal window. +### Step-by-Step Guide -You can add files to `/var/snap/hplip-printer-app/common/usb/` for -additional USB quirk rules. Edit the existing files only for quick -tests, as they get replaced at every update of the Snap (to introduce -new rules). +#### 1. Build phplip-printer-app rock: -You can edit the `/var/snap/hplip-printer-app/common/cups/snmp.conf` -file for configuring SNMP network printer discovery. +The first step is to build the Rock from the `rockcraft.yaml`. This image will contain all the configurations and dependencies required to run hplip-printer-app. +Open your terminal and navigate to the directory containing your `rockcraft.yaml`, then run the following command: -## BUILDING WITHOUT SNAP - -You can also do a "quick-and-dirty" build without snapping and without -needing to install [PAPPL](https://www.msweet.org/pappl), -[cups-filters 2.x](https://github.com/OpenPrinting/cups-filters), and -[pappl-retrofit](https://github.com/OpenPrinting/pappl-retrofit) into -your system. You need a directory with the latest GIT snapshot of -PAPPL, the latest GIT snapshot of cups-filters, and the latest GIT -snapshot of pappl-retrofit (master branches of each). They all need to -be compiled (`./autogen.sh; ./configure; make`), installing not -needed. Also install the header files of all needed libraries -(installing "libcups2-dev" should do it). - -In the directory with hplip-printer-app.c run the command line - -``` -gcc -o hplip-printer-app hplip-printer-app.c $PAPPL_SRC/pappl/libpappl.a $CUPS_FILTERS_SRC/.libs/libppd.a $CUPS_FILTERS_SRC/.libs/libcupsfilters.a $PAPPL_RETROFIT_SRC/.libs/libpappl-retrofit.a -ldl -lpthread -lppd -lcups -lavahi-common -lavahi-client -lgnutls -ljpeg -lpng16 -ltiff -lz -lm -lusb-1.0 -lpam -lqpdf -lstdc++ -I. -I$PAPPL_SRC/pappl -I$CUPS_FILTERS_SRC/ppd -I$CUPS_FILTERS_SRC/cupsfilters -I$PAPPL_RETROFIT_SRC/pappl/retrofit -L$CUPS_FILTERS_SRC/.libs/ -L$PAPPL_RETROFIT_SRC/.libs/ +```sh +rockcraft pack -v ``` -There is also a Makefile, but this needs PAPPL, cups-filters 2.x, and -pappl-retrofit to be installed into your system. +#### 2. Compile to Docker Image: -Run +Once the rock is built, you need to compile docker image from it. -``` -./hplip-printer-app --help -``` - -When running the non-snapped version, by default, PPD files are -searched for in - -``` -/usr/share/ppd/ -/usr/lib/cups/driver/ -/var/lib/hplip-printer-app/ppd/ +```sh +sudo rockcraft.skopeo --insecure-policy copy oci-archive: docker-daemon:hplip-printer-app:latest ``` -You can set the `PPD_PATHS` environment variable to search other -places instead: +#### Run the hplip-printer-app Docker Container: +```sh +sudo docker run --rm -d --name hplip-printer-app -p :8000 \ + hplip-printer-app:latest ``` -PPD_PATHS=/path/to/my/ppds:/my/second/place ./hplip-printer-app server -``` - -Simply put a colon-separated list of any amount of paths into the -variable. Creating a wrapper script is recommended. - -Note that only PPD files for the `hpcups` driver of HPLIP are -considred, other PPD files are ignored. - -Printers are only discovered via the `hp` backend of HPLIP (USB) or -the `hp-probe` utility of HPLIP (network). For the latter a wrapper -script named `HP` is included which makes the utility be used like a -CUPS backend (discovery mode only). This especially makes only HP and -Apollo printers being discovered. Printers from other manufacturers -are not supported. - -Jobs are filtered through `hpcups` and send to the printer via the -`hp` backend (both USB and network). - -The standard (not HPLIP's) backends provided as alternative in this -Printer Application are CUPS' backends and not PAPPL's, meaning that -for USB printers CUPS' USB quirk workarounds for compatibility -problems are used, network printers can also be used with IPP, IPPS, -and LPD protocols, and SNMP printer discovery is configurable. - -USB Quirk rules in `/usr/share/cups/usb` and the `/etc/cups/snmp.conf` -file can get edited if needed. - -Make sure you have HPLIP installed and if you want to use standard -backends, CUPS (at least its backends). -You also need Ghostscript to print PDF or PostScript jobs. - -For access to the test page `testpage.ps` use the TESTPAGE_DIR -environment variable: - -``` -TESTPAGE_DIR=`pwd` PPD_PATHS=/path/to/my/ppds:/my/second/place ./hplip-printer-app server -``` +### Setting up -or for your own creation of a test page (PostScript, PDF, PNG, JPEG, -Apple Raster, PWG Raster): +Enter the web interface +```sh +http://localhost:/ ``` -TESTPAGE=/path/to/my/testpage/my_testpage.ps PPD_PATHS=/path/to/my/ppds:/my/second/place ./hplip-printer-app server -``` - - -## LEGAL STUFF -The HPLIP Printer Application is Copyright © 2020 by Till Kamppeter. - -It is derived from the HP PCL Printer Application, a first working model of -a raster Printer Application using PAPPL. It is available here: - -https://github.com/michaelrsweet/hp-printer-app - -The HP PCL Printer Application is Copyright © 2019-2020 by Michael R Sweet. - -This software is licensed under the Apache License Version 2.0 with an exception -to allow linking against GPL2/LGPL2 software (like older versions of CUPS). See -the files "LICENSE" and "NOTICE" for more information. +Use the web interface to add a printer. Supply a name, select the +discovered printer, then select make and model. Also set the installed +accessories, loaded media and the option defaults. If the printer is a +PostScript printer, accessory configuration and option defaults can +also often get polled from the printer. \ No newline at end of file From 746acb924acd76a527316058b033ef8c1873d73a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 07:23:19 +0000 Subject: [PATCH 05/13] Update snap version/tag --- rockcraft.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft.yaml b/rockcraft.yaml index b043b99..25086be 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -1,6 +1,6 @@ name: hplip-printer-app base: ubuntu@22.04 -version: '3.22.10-8' +version: '3.22.10-9' summary: HPLIP Printer Application description: | The HPLIP Printer Application is a PAPPL (Printer Application @@ -238,7 +238,7 @@ parts: cups: source: https://github.com/OpenPrinting/cups source-type: git - source-tag: 'v2.4.10' + source-tag: 'v2.4.11' source-depth: 1 # ext:updatesnap # version-format: From 368e2bd4560d817ed109b6d4e360291c943d3055 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 07:22:30 +0000 Subject: [PATCH 06/13] Update snap version/tag --- rockcraft.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft.yaml b/rockcraft.yaml index 25086be..2da4894 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -1,6 +1,6 @@ name: hplip-printer-app base: ubuntu@22.04 -version: '3.22.10-9' +version: '3.22.10-10' summary: HPLIP Printer Application description: | The HPLIP Printer Application is a PAPPL (Printer Application @@ -37,7 +37,7 @@ parts: pappl: source: https://github.com/michaelrsweet/pappl source-type: git - source-tag: 'v1.4.6' + source-tag: 'v1.4.7' source-depth: 1 # ext:updatesnap # version-format: From 02ddf7e7257423856019298a8aabac3376d4ee3d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Oct 2024 07:21:54 +0000 Subject: [PATCH 07/13] Update snap version/tag --- rockcraft.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rockcraft.yaml b/rockcraft.yaml index 2da4894..7487c3b 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -1,6 +1,6 @@ name: hplip-printer-app base: ubuntu@22.04 -version: '3.22.10-10' +version: '3.22.10-11' summary: HPLIP Printer Application description: | The HPLIP Printer Application is a PAPPL (Printer Application @@ -346,7 +346,7 @@ parts: libcupsfilters: source: https://github.com/OpenPrinting/libcupsfilters source-type: git - source-tag: '2.0.0' + source-tag: '2.1.0' source-depth: 1 # ext:updatesnap # version-format: @@ -413,7 +413,7 @@ parts: libppd: source: https://github.com/OpenPrinting/libppd source-type: git - source-tag: '2.0.0' + source-tag: '2.1.0' source-depth: 1 # ext:updatesnap # version-format: From e4f6074aa449851c249bfff83dd1cab87a03ebd4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 07:22:35 +0000 Subject: [PATCH 08/13] Update snap version/tag --- rockcraft.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft.yaml b/rockcraft.yaml index 7487c3b..b0d620b 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -1,6 +1,6 @@ name: hplip-printer-app base: ubuntu@22.04 -version: '3.22.10-11' +version: '3.22.10-12' summary: HPLIP Printer Application description: | The HPLIP Printer Application is a PAPPL (Printer Application @@ -195,7 +195,7 @@ parts: #source: https://git.ghostscript.com/ghostpdl.git source: https://github.com/ArtifexSoftware/ghostpdl.git source-type: git - source-tag: 'ghostpdl-10.03.0rc1_test' + source-tag: 'ghostpdl-10.05.0-test-base-001' source-depth: 1 # ext:updatesnap # version-format: From 0fbca5f95bbfbd927b2a271817cb03c8872c288c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 07:23:07 +0000 Subject: [PATCH 09/13] Update snap version/tag --- rockcraft.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft.yaml b/rockcraft.yaml index b0d620b..0f304a1 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -1,6 +1,6 @@ name: hplip-printer-app base: ubuntu@22.04 -version: '3.22.10-12' +version: '3.22.10-13' summary: HPLIP Printer Application description: | The HPLIP Printer Application is a PAPPL (Printer Application @@ -37,7 +37,7 @@ parts: pappl: source: https://github.com/michaelrsweet/pappl source-type: git - source-tag: 'v1.4.7' + source-tag: 'v1.4.8' source-depth: 1 # ext:updatesnap # version-format: From 543d82682435ff57927fbc41838140baa9ae0093 Mon Sep 17 00:00:00 2001 From: Rudra-IITM Date: Sun, 15 Dec 2024 20:00:54 +0530 Subject: [PATCH 10/13] Add CI pipeline, PORT flag support, and README updates for containerized setup - Added CI pipeline for building and testing `Rock` and `Snap` packages. - Implemented `PORT` flag support for custom port configuration in containerized printer-app. - Updated `README` with container setup instructions, including `--network host` mode and `PORT` usage. - Introduced `start-server.sh` for easier server initialization. --- .github/workflows/auto-update.yml | 30 +++++- .github/workflows/ci.yml | 31 +++++++ .github/workflows/registry-actions.yml | 93 +++++++++---------- README.md | 28 ++++-- .../cups-dnssd-backend-socket-only.patch | 0 .../hplip-plugin-firmware-load-path.patch | 0 .../hplip-plugin-library-load-path.patch | 0 rockcraft.yaml | 9 +- scripts/start-server.sh | 12 +++ 9 files changed, 140 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/ci.yml rename {patch-files => patches}/cups-dnssd-backend-socket-only.patch (100%) rename {patch-files => patches}/hplip-plugin-firmware-load-path.patch (100%) rename {patch-files => patches}/hplip-plugin-library-load-path.patch (100%) create mode 100644 scripts/start-server.sh diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml index f85effe..bba75a8 100644 --- a/.github/workflows/auto-update.yml +++ b/.github/workflows/auto-update.yml @@ -2,19 +2,39 @@ name: Push new tag update to stable branch on: schedule: - # Daily for now - - cron: "9 7 * * *" + - cron: '9 7 * * *' workflow_dispatch: + inputs: + workflow_choice: + description: "Choose YAML to update" + required: true + default: "both" + type: choice + options: + - snapcraft + - rockcraft + - both jobs: - update-snapcraft-yaml: + update-yamls: runs-on: ubuntu-latest steps: - name: Checkout this repo uses: actions/checkout@v3 - - name: Run desktop-snaps action + + - name: Run desktop-snaps action (Snapcraft) + if: ${{ github.event_name == 'schedule' || github.event.inputs.workflow_choice == 'snapcraft' || github.event.inputs.workflow_choice == 'both' }} + uses: ubuntu/desktop-snaps@stable + with: + token: ${{ secrets.GITHUB_TOKEN }} + repo: ${{ github.repository }} + version-schema: '^(\d{8})' + + - name: Run desktop-snaps action (Rockcraft) + if: ${{ github.event_name == 'schedule' || github.event.inputs.workflow_choice == 'rockcraft' || github.event.inputs.workflow_choice == 'both' }} uses: ubuntu/desktop-snaps@stable with: token: ${{ secrets.GITHUB_TOKEN }} repo: ${{ github.repository }} - rock-version-schema: '^debian/(\d+\.\d+\.\d+)' + rock-version-schema: '^(\d{8})' + yaml-path: 'rockcraft.yaml' \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f8ceb18 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI Pipeline for ps-printer-app + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + build-rock: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Pack with Rockcraft + uses: canonical/craft-actions/rockcraft-pack@main + id: rockcraft + + build-snap: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build Snap Package + uses: snapcore/action-build@v1 + id: snapcraft diff --git a/.github/workflows/registry-actions.yml b/.github/workflows/registry-actions.yml index 0467f15..60a3f4d 100644 --- a/.github/workflows/registry-actions.yml +++ b/.github/workflows/registry-actions.yml @@ -1,4 +1,4 @@ -name: Pack and Publish OCI Image to Docker Registry +name: Pack and Publish OCI Image to Docker Registry and GitHub Packages on: push: @@ -14,6 +14,11 @@ on: options: - edge - stable + - both + workflow_run: + workflows: ["Push new tag update to stable branch"] + types: + - completed jobs: build-rock: @@ -25,6 +30,8 @@ jobs: - name: Pack with Rockcraft uses: canonical/craft-actions/rockcraft-pack@main id: rockcraft + with: + path: rock - name: Upload Rock Artifact uses: actions/upload-artifact@v4 @@ -32,9 +39,9 @@ jobs: name: cups-rock path: ${{ steps.rockcraft.outputs.rock }} - publish-rock-edge: + publish-rock: needs: build-rock - if: github.ref_name == 'main' && (github.event.inputs.workflow_choice == 'edge' || github.event_name == 'push') + if: github.ref_name == 'main' runs-on: ubuntu-latest steps: - name: Checkout repository @@ -57,13 +64,21 @@ jobs: sudo systemctl enable docker sudo systemctl is-active --quiet docker || sudo systemctl start docker - - name: Log in to Docker Hub + # - name: Log in to Docker Hub + # uses: docker/login-action@v3.2.0 + # with: + # username: ${{ secrets.DOCKER_USERNAME }} + # password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to GitHub Packages uses: docker/login-action@v3.2.0 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and Push Docker Image + - name: Build and Push Docker Image (Edge & Latest Channel) + if: github.event.inputs.workflow_choice == 'edge' || github.event.inputs.workflow_choice == 'both' || github.event_name == 'push' || github.event_name == 'workflow_run' env: USERNAME: ${{ secrets.DOCKER_USERNAME }} run: | @@ -71,47 +86,29 @@ jobs: VERSION="$(yq '.version' rockcraft.yaml)" ROCK="$(ls *.rock | tail -n 1)" sudo rockcraft.skopeo --insecure-policy copy oci-archive:"${ROCK}" docker-daemon:"${USERNAME}/${IMAGE}:${VERSION}-edge" - docker push ${USERNAME}/${IMAGE}:${VERSION}-edge - docker tag ${USERNAME}/${IMAGE}:${VERSION}-edge ${USERNAME}/${IMAGE}:latest - docker push ${USERNAME}/${IMAGE}:latest - - publish-rock-stable: - needs: build-rock - if: github.ref_name == 'main' && github.event.inputs.workflow_choice == 'stable' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Download Rock Artifact - uses: actions/download-artifact@v4 - with: - name: cups-rock - - - name: Install Dependencies - run: | - sudo snap install rockcraft --classic - sudo snap install docker - sudo snap install yq - - - name: Ensure Docker Daemon is Running - run: | - sudo systemctl start docker - sudo systemctl enable docker - sudo systemctl is-active --quiet docker || sudo systemctl start docker - - - name: Log in to Docker Hub - uses: docker/login-action@v3.2.0 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and Push Docker Image + # Push to Docker Hub + # docker push ${USERNAME}/${IMAGE}:${VERSION}-edge + # docker tag ${USERNAME}/${IMAGE}:${VERSION}-edge ${USERNAME}/${IMAGE}:latest + # docker push ${USERNAME}/${IMAGE}:latest + # Push to GitHub Packages + GITHUB_IMAGE="ghcr.io/${{ github.repository_owner }}/${IMAGE}" + docker tag ${USERNAME}/${IMAGE}:${VERSION}-edge ${GITHUB_IMAGE}:${VERSION}-edge + docker push ${GITHUB_IMAGE}:${VERSION}-edge + docker tag ${GITHUB_IMAGE}:${VERSION}-edge ${GITHUB_IMAGE}:latest + docker push ${GITHUB_IMAGE}:latest + + - name: Build and Push Docker Image (Stable Channel) + if: github.event.inputs.workflow_choice == 'stable' || github.event.inputs.workflow_choice == 'both' env: USERNAME: ${{ secrets.DOCKER_USERNAME }} run: | - IMAGE="$(yq '.name' rockcraft.yaml)" - VERSION="$(yq '.version' rockcraft.yaml)" - ROCK="$(ls *.rock | tail -n 1)" - sudo rockcraft.skopeo --insecure-policy copy oci-archive:"${ROCK}" docker-daemon:"${USERNAME}/${IMAGE}:${VERSION}-stable" - docker push ${USERNAME}/${IMAGE}:${VERSION}-stable + IMAGE="$(yq '.name' rockcraft.yaml)" + VERSION="$(yq '.version' rockcraft.yaml)" + ROCK="$(ls *.rock | tail -n 1)" + sudo rockcraft.skopeo --insecure-policy copy oci-archive:"${ROCK}" docker-daemon:"${USERNAME}/${IMAGE}:${VERSION}-stable" + # Push to Docker Hub + # docker push ${USERNAME}/${IMAGE}:${VERSION}-stable + # Push to GitHub Packages + GITHUB_IMAGE="ghcr.io/${{ github.repository_owner }}/${IMAGE}" + docker tag ${USERNAME}/${IMAGE}:${VERSION}-stable ${GITHUB_IMAGE}:${VERSION}-stable + docker push ${GITHUB_IMAGE}:${VERSION}-stable diff --git a/README.md b/README.md index 45ab170..f69d26f 100644 --- a/README.md +++ b/README.md @@ -148,16 +148,22 @@ first. The first step is to pull the hplip-printer-app Docker image from Docker Hub. ```sh -sudo docker pull openprinting/hplip-printer-app + sudo docker pull openprinting/hplip-printer-app ``` #### 2. Start the hplip-printer-app Container ##### Run the following Docker command to run the hplip-printer-app image: ```sh -sudo docker run --rm -d --name hplip-printer-app -p :8000 \ - openprinting/hplip-printer-app:latest + sudo docker run --rm -d \ + --name hplip-printer-app \ + --network host \ + -e PORT: \ + openprinting/hplip-printer-app:latest ``` +- `PORT` is an optional environment variable used to start the printer-app on a specified port. If not provided, it will start on the default port 8000 or, if port 8000 is busy, on 8001 and so on. +- **The container must be started in `--network host` mode** to allow the Printer-Application instance inside the container to access and discover printers available in the local network where the host system is in. +- Alternatively using the internal network of the Docker instance (`-p :8000` instead of `--network host -e PORT:`) only gives access to local printers running on the host system itself. ## Setting Up and Running hplip-printer-app locally @@ -167,7 +173,7 @@ sudo docker run --rm -d --name hplip-printer-app -p :8000 \ 2. **Rockcraft**: Rockcraft should be installed. You can install Rockcraft using the following command: ```sh -sudo snap install rockcraft --classic + sudo snap install rockcraft --classic ``` 3. **Skopeo**: Skopeo should be installed to compile `.rock` files into Docker images.
@@ -182,7 +188,7 @@ The first step is to build the Rock from the `rockcraft.yaml`. This image will c Open your terminal and navigate to the directory containing your `rockcraft.yaml`, then run the following command: ```sh -rockcraft pack -v + rockcraft pack -v ``` #### 2. Compile to Docker Image: @@ -190,15 +196,21 @@ rockcraft pack -v Once the rock is built, you need to compile docker image from it. ```sh -sudo rockcraft.skopeo --insecure-policy copy oci-archive: docker-daemon:hplip-printer-app:latest + sudo rockcraft.skopeo --insecure-policy copy oci-archive: docker-daemon:hplip-printer-app:latest ``` #### Run the hplip-printer-app Docker Container: ```sh -sudo docker run --rm -d --name hplip-printer-app -p :8000 \ - hplip-printer-app:latest + sudo docker run --rm -d \ + --name hplip-printer-app \ + --network host \ + -e PORT: \ + hplip-printer-app:latest ``` +- `PORT` is an optional environment variable used to start the printer-app on a specified port. If not provided, it will start on the default port 8000 or, if port 8000 is busy, on 8001 and so on. +- **The container must be started in `--network host` mode** to allow the Printer-Application instance inside the container to access and discover printers available in the local network where the host system is in. +- Alternatively using the internal network of the Docker instance (`-p :8000` instead of `--network host -e PORT:`) only gives access to local printers running on the host system itself. ### Setting up diff --git a/patch-files/cups-dnssd-backend-socket-only.patch b/patches/cups-dnssd-backend-socket-only.patch similarity index 100% rename from patch-files/cups-dnssd-backend-socket-only.patch rename to patches/cups-dnssd-backend-socket-only.patch diff --git a/patch-files/hplip-plugin-firmware-load-path.patch b/patches/hplip-plugin-firmware-load-path.patch similarity index 100% rename from patch-files/hplip-plugin-firmware-load-path.patch rename to patches/hplip-plugin-firmware-load-path.patch diff --git a/patch-files/hplip-plugin-library-load-path.patch b/patches/hplip-plugin-library-load-path.patch similarity index 100% rename from patch-files/hplip-plugin-library-load-path.patch rename to patches/hplip-plugin-library-load-path.patch diff --git a/rockcraft.yaml b/rockcraft.yaml index 0f304a1..4833a13 100644 --- a/rockcraft.yaml +++ b/rockcraft.yaml @@ -27,9 +27,9 @@ services: startup: enabled hplip-printer-app: - command: hplip-printer-app -o log-file=/hplip-printer-app.log server + command: /scripts/start-server.sh override: replace - on-failure: restart + on-failure: shutdown startup: enabled after: [dbus] @@ -851,6 +851,7 @@ parts: source: scripts/ organize: run-dbus.sh: /scripts/run-dbus.sh + start-server.sh: /scripts/start-server.sh override-prime: | set -eux craftctl default @@ -858,3 +859,7 @@ parts: if [ -f "$CRAFT_PRIME/scripts/run-dbus.sh" ]; then chmod +x "$CRAFT_PRIME/scripts/run-dbus.sh" fi + # Ensure the start-server.sh script has executable permissions + if [ -f "$CRAFT_PRIME/scripts/start-server.sh" ]; then + chmod +x "$CRAFT_PRIME/scripts/start-server.sh" + fi diff --git a/scripts/start-server.sh b/scripts/start-server.sh new file mode 100644 index 0000000..6117fd2 --- /dev/null +++ b/scripts/start-server.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -eux + +# Precheck: Ensure PORT is a number or undefined +if [ -n "${PORT:-}" ]; then + if ! echo "$PORT" | grep -Eq '^[0-9]+$'; then + echo "Error: PORT must be a valid number" >&2 + exit 1 + fi +fi + +hplip-printer-app -o log-file=/hplip-printer-app.log ${PORT:+-o server-port=$PORT} server From 353ce5fcbcea9215ef495a35339f616598a27f17 Mon Sep 17 00:00:00 2001 From: rudra-iitm Date: Sun, 15 Dec 2024 21:43:13 +0530 Subject: [PATCH 11/13] Update README: Correct environment variable (PORT) passed to the container. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3240b28..9059411 100644 --- a/README.md +++ b/README.md @@ -268,12 +268,12 @@ Run the following Docker command to run the hplip-printer-app image: sudo docker run --rm -d \ --name hplip-printer-app \ --network host \ - -e PORT: \ + -e PORT= \ openprinting/hplip-printer-app:latest ``` - `PORT` is an optional environment variable used to start the printer-app on a specified port. If not provided, it will start on the default port 8000 or, if port 8000 is busy, on 8001 and so on. - **The container must be started in `--network host` mode** to allow the Printer-Application instance inside the container to access and discover printers available in the local network where the host system is in. -- Alternatively using the internal network of the Docker instance (`-p :8000` instead of `--network host -e PORT:`) only gives access to local printers running on the host system itself. +- Alternatively using the internal network of the Docker instance (`-p :8000` instead of `--network host -e PORT=`) only gives access to local printers running on the host system itself. ### Setting Up and Running hplip-printer-app locally @@ -317,12 +317,12 @@ Once the rock is built, you need to compile docker image from it. sudo docker run --rm -d \ --name hplip-printer-app \ --network host \ - -e PORT: \ + -e PORT= \ hplip-printer-app:latest ``` - `PORT` is an optional environment variable used to start the printer-app on a specified port. If not provided, it will start on the default port 8000 or, if port 8000 is busy, on 8001 and so on. - **The container must be started in `--network host` mode** to allow the Printer-Application instance inside the container to access and discover printers available in the local network where the host system is in. -- Alternatively using the internal network of the Docker instance (`-p :8000` instead of `--network host -e PORT:`) only gives access to local printers running on the host system itself. +- Alternatively using the internal network of the Docker instance (`-p :8000` instead of `--network host -e PORT=`) only gives access to local printers running on the host system itself. #### Setting up From 7c3e78ec573631804e810e8537e4ea3b1c65872f Mon Sep 17 00:00:00 2001 From: rudra-iitm Date: Sun, 15 Dec 2024 22:06:50 +0530 Subject: [PATCH 12/13] Fixes typo --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 107a435..4a8e443 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI Pipeline for ps-printer-app +name: CI Pipeline for hplip-printer-app on: push: From d9b034deb471fe49d1b58c0b7b58d6b678610e1f Mon Sep 17 00:00:00 2001 From: rudra-iitm Date: Sun, 15 Dec 2024 22:58:18 +0530 Subject: [PATCH 13/13] Master master reference in `registry-actions.yml` workflow --- .github/workflows/registry-actions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/registry-actions.yml b/.github/workflows/registry-actions.yml index 46052c1..6c909d5 100644 --- a/.github/workflows/registry-actions.yml +++ b/.github/workflows/registry-actions.yml @@ -40,7 +40,7 @@ jobs: publish-rock: needs: build-rock - if: github.ref_name == 'main' + if: github.ref_name == 'main' || github.ref_name == 'master' runs-on: ubuntu-latest steps: - name: Checkout repository