pax_global_header00006660000000000000000000000064135321607340014516gustar00rootroot0000000000000052 comment=e28e9b3fcbf1853fcd18aa0acad922e5af17a268 cpdb-backend-cups-1.1.1/000077500000000000000000000000001353216073400147635ustar00rootroot00000000000000cpdb-backend-cups-1.1.1/.gitignore000066400000000000000000000003661353216073400167600ustar00rootroot00000000000000*.o *.pdf cups .vscode/ .gdb_history gmon.out Makefile.in .gitignore aclocal.m4 Makefile */Makefile depcomp install-sh missing aclocal.* compile autom4* config.log config.status configure data/org.openprinting.Backend.CUPS.service src/.deps tmp/ cpdb-backend-cups-1.1.1/LICENSE.md000066400000000000000000000020571353216073400163730ustar00rootroot00000000000000MIT License Copyright (c) 2017 Nilanjana Lodh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cpdb-backend-cups-1.1.1/Makefile.am000066400000000000000000000000561353216073400170200ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign SUBDIRS = src data cpdb-backend-cups-1.1.1/README.md000066400000000000000000000025351353216073400162470ustar00rootroot00000000000000# CUPS Common Print Dialog Backend This repository hosts the code for the CUPS **C**ommon **P**rint **D**ialog **B**ackend. This backend manages and provides information about CUPS and IPP printing destinations to the printing dialog. ## Background The [Common Printing Dialog](https://wiki.ubuntu.com/CommonPrintingDialog) project aims to provide a uniform, GUI toolkit independent printing experience on Linux Desktop Environments. ## Dependencies - [cpdb-libs](https://github.com/OpenPrinting/cpdb-libs) : Version >= 1.2.0 - [CUPS](https://github.com/apple/cups/releases) : Version >= 2.2 Install bleeding edge release from [here](https://github.com/apple/cups/releases). OR `sudo apt install cups libcups2-dev` - GLIB 2.0 : `sudo apt install libglib2.0-dev` ## Build and installation $ ./autogen.sh $ ./configure $ make $ sudo make install ## Following the development and updating The current source code you find on the [OpenPrinting GitHub](https://github.com/OpenPrinting/cpdb-backend-cups). ## Running The backend is auto-activated when a frontend runs; So no need to run it explicitly. However, if you wish to see the debug statements at the backend, you can run `/usr/lib/print-backends/cups`. ## More Info [Nilanjana Lodh's Google Summer of Code 2017 Final Report](https://nilanjanalodh.github.io/common-print-dialog-gsoc17/) cpdb-backend-cups-1.1.1/autogen.sh000077500000000000000000000000761353216073400167670ustar00rootroot00000000000000#! /bin/sh aclocal \ && automake --add-missing \ && autoconf cpdb-backend-cups-1.1.1/configure.ac000066400000000000000000000022731353216073400172550ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) AC_INIT([cpdb-backend-cups],[1.1.1],[nilanjanalodh@gmail.com],[CPDB - CUPS Backend], [https://github.com/OpenPrinting/cpdb-backend-cups]) AM_INIT_AUTOMAKE([-Wall foreign]) AC_CONFIG_SRCDIR([src/print_backend_cups.c]) : ${CFLAGS=""} # Check for a C compiler AC_PROG_CC # Checks for backend library PKG_CHECK_MODULES([CPDBBACKEND],[cpdb-libs-backend >= 1]) PKG_CHECK_MODULES([GIO],[gio-2.0]) PKG_CHECK_MODULES([GIOUNIX],[gio-unix-2.0]) PKG_CHECK_MODULES([GLIB],[glib-2.0]) # Checks for header files. AC_CHECK_HEADERS([stdlib.h string.h cups/cups.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # Checks for library functions AC_FUNC_MALLOC # The Dbus service directory DBUS_SERVICES_DIR="/usr/share/dbus-1/services" AC_SUBST(DBUS_SERVICES_DIR) # The info directory which will be read by the frontend INFO_DIR="/usr/share/print-backends" AC_SUBST(INFO_DIR) # The directory for the backend executables CPDB_BACKEND_DIR="$libdir/print-backends" AC_SUBST(CPDB_BACKEND_DIR) AC_OUTPUT([Makefile src/Makefile data/Makefile]) cpdb-backend-cups-1.1.1/data/000077500000000000000000000000001353216073400156745ustar00rootroot00000000000000cpdb-backend-cups-1.1.1/data/Makefile.am000066400000000000000000000006161353216073400177330ustar00rootroot00000000000000# Dbus service file servicedir = $(DBUS_SERVICES_DIR) service_in_files = org.openprinting.Backend.CUPS.service.in service_DATA = $(service_in_files:.service.in=.service) $(service_DATA): $(service_in_files) Makefile @sed -e "s|\@cpdb_backend_dir\@|$(CPDB_BACKEND_DIR)|" $<> $@ info_backenddir = $(INFO_DIR) info_backend_DATA = org.openprinting.Backend.CUPS clean-local: rm -rf $(service_DATA) cpdb-backend-cups-1.1.1/data/org.openprinting.Backend.CUPS000066400000000000000000000000021353216073400232100ustar00rootroot00000000000000/ cpdb-backend-cups-1.1.1/data/org.openprinting.Backend.CUPS.service.in000066400000000000000000000001201353216073400252550ustar00rootroot00000000000000[D-BUS Service] Name=org.openprinting.Backend.CUPS Exec=@cpdb_backend_dir@/cups cpdb-backend-cups-1.1.1/src/000077500000000000000000000000001353216073400155525ustar00rootroot00000000000000cpdb-backend-cups-1.1.1/src/Makefile.am000066400000000000000000000006311353216073400176060ustar00rootroot00000000000000backenddir = $(CPDB_BACKEND_DIR) backend_PROGRAMS = cups cups_SOURCES = print_backend_cups.c backend_helper.c cups_CPPFLAGS = $(CPDBBACKEND_CFLAGS) cups_CPPFLAGS += $(GLIB_CFLAGS) cups_CPPFLAGS += $(GIO_CFLAGS) cups_CPPFLAGS += $(GIOUNIX_CFLAGS) cups_LDADD = $(CPDBBACKEND_LIBS) cups_LDADD += -lcups -lpthread -lm -lcrypt cups_LDADD += $(GLIB_LIBS) cups_LDADD += $(GIO_LIBS) cups_LDADD += $(GIOUNIX_LIBS) cpdb-backend-cups-1.1.1/src/backend_helper.c000066400000000000000000001037221353216073400206510ustar00rootroot00000000000000#include "backend_helper.h" Mappings *map; /*****************BackendObj********************************/ BackendObj *get_new_BackendObj() { map = get_new_Mappings(); BackendObj *b = (BackendObj *)(malloc(sizeof(BackendObj))); b->dbus_connection = NULL; b->dialogs = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)free_string, (GDestroyNotify)free_Dialog); b->num_frontends = 0; b->obj_path = NULL; b->default_printer = NULL; return b; } /** Don't free the returned value; it is owned by BackendObj */ char *get_default_printer(BackendObj *b) { /** If it was previously querie, don't query again */ if (b->default_printer) { return b->default_printer; } /**first query to see if the user default printer is set**/ int num_dests; cups_dest_t *dests; num_dests = cupsGetDests2(CUPS_HTTP_DEFAULT, &dests); /** Get the list of all destinations */ cups_dest_t *dest = cupsGetDest(NULL, NULL, num_dests, dests); /** Get the default one */ if (dest) { /** Return the user default printer */ char *def = get_string_copy(dest->name); cupsFreeDests(num_dests, dests); b->default_printer = def; return def; } cupsFreeDests(num_dests, dests); /** Then query the system default printer **/ ipp_t *request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT); ipp_t *response; ipp_attribute_t *attr; if ((response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) != NULL) { if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL) { b->default_printer = get_string_copy(ippGetString(attr, 0, NULL)); ippDelete(response); return b->default_printer; } } ippDelete(response); b->default_printer = get_string_copy("NA"); return b->default_printer; } void connect_to_dbus(BackendObj *b, char *obj_path) { b->obj_path = obj_path; GError *error = NULL; g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(b->skeleton), b->dbus_connection, obj_path, &error); if (error) { MSG_LOG("Error connecting CUPS Backend to D-Bus.\n", ERR); } } void add_frontend(BackendObj *b, const char *dialog_name) { Dialog *d = get_new_Dialog(); g_hash_table_insert(b->dialogs, get_string_copy(dialog_name), d); b->num_frontends++; } void remove_frontend(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, dialog_name)); if (d) { g_hash_table_remove(b->dialogs, dialog_name); b->num_frontends--; } g_message("Removed Frontend entry for %s", dialog_name); } gboolean no_frontends(BackendObj *b) { if ((b->num_frontends) == 0) return TRUE; return FALSE; } Dialog *find_dialog(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, dialog_name)); return d; } int *get_dialog_cancel(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, dialog_name)); return &d->cancel; } void set_dialog_cancel(BackendObj *b, const char *dialog_name) { int *x = get_dialog_cancel(b, dialog_name); *x = 1; } void reset_dialog_cancel(BackendObj *b, const char *dialog_name) { int *x = get_dialog_cancel(b, dialog_name); *x = 0; } void set_hide_remote_printers(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, dialog_name)); d->hide_remote = TRUE; } void unset_hide_remote_printers(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, dialog_name)); d->hide_remote = FALSE; } void set_hide_temp_printers(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, dialog_name)); d->hide_temp = TRUE; } void unset_hide_temp_printers(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, dialog_name)); d->hide_temp = FALSE; } gboolean dialog_contains_printer(BackendObj *b, const char *dialog_name, const char *printer_name) { Dialog *d = g_hash_table_lookup(b->dialogs, dialog_name); if (d == NULL || d->printers == NULL) { char *msg = malloc(sizeof(char) * (strlen(dialog_name) + 50)); sprintf(msg, "Can't retrieve printers for dialog %s.\n", dialog_name); MSG_LOG(msg, ERR); free(msg); return FALSE; } if (g_hash_table_contains(d->printers, printer_name)) return TRUE; return FALSE; } PrinterCUPS *add_printer_to_dialog(BackendObj *b, const char *dialog_name, const cups_dest_t *dest) { char *printer_name = get_string_copy(dest->name); Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if (d == NULL) { char *msg = malloc(sizeof(char) * (strlen(dialog_name) + 50)); sprintf(msg, "Invalid dialog name %s.\n", dialog_name); MSG_LOG(msg, ERR); free(msg); return NULL; } PrinterCUPS *p = get_new_PrinterCUPS(dest); g_hash_table_insert(d->printers, printer_name, p); return p; } void remove_printer_from_dialog(BackendObj *b, const char *dialog_name, const char *printer_name) { Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if (d == NULL) { char *msg = malloc(sizeof(char) * (strlen(printer_name) + 50)); sprintf(msg, "Unable to remove printer %s.\n", printer_name); MSG_LOG(msg, WARN); free(msg); return; } g_hash_table_remove(d->printers, printer_name); } void send_printer_added_signal(BackendObj *b, const char *dialog_name, cups_dest_t *dest) { if (dest == NULL) { MSG_LOG("Failed to send printer added signal.\n", ERR); exit(EXIT_FAILURE); } char *printer_name = get_string_copy(dest->name); GVariant *gv = g_variant_new(PRINTER_ADDED_ARGS, printer_name, //id printer_name, //name cups_retrieve_string(dest, "printer-info"), //info cups_retrieve_string(dest, "printer-location"), //location cups_retrieve_string(dest, "printer-make-and-model"), cups_is_accepting_jobs(dest), cups_printer_state(dest), "CUPS"); GError *error = NULL; g_dbus_connection_emit_signal(b->dbus_connection, dialog_name, b->obj_path, "org.openprinting.PrintBackend", PRINTER_ADDED_SIGNAL, gv, &error); g_assert_no_error(error); } void send_printer_removed_signal(BackendObj *b, const char *dialog_name, const char *printer_name) { GError *error = NULL; g_dbus_connection_emit_signal(b->dbus_connection, dialog_name, b->obj_path, "org.openprinting.PrintBackend", PRINTER_REMOVED_SIGNAL, g_variant_new("(ss)", printer_name, "CUPS"), &error); g_assert_no_error(error); } void notify_removed_printers(BackendObj *b, const char *dialog_name, GHashTable *new_table) { Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); GHashTable *prev = d->printers; GList *prevlist = g_hash_table_get_keys(prev); printf("Notifying removed printers.\n"); gpointer printer_name = NULL; while (prevlist) { printer_name = (char *)(prevlist->data); if (!g_hash_table_contains(new_table, (gchar *)printer_name)) { g_message("Printer %s removed\n", (char *)printer_name); send_printer_removed_signal(b, dialog_name, (char *)printer_name); remove_printer_from_dialog(b, dialog_name, (char *)printer_name); } prevlist = prevlist->next; } } void notify_added_printers(BackendObj *b, const char *dialog_name, GHashTable *new_table) { GHashTableIter iter; Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); GHashTable *prev = d->printers; printf("Notifying added printers.\n"); gpointer printer_name; gpointer value; cups_dest_t *dest = NULL; g_hash_table_iter_init(&iter, new_table); while (g_hash_table_iter_next(&iter, &printer_name, &value)) { if (!g_hash_table_contains(prev, (gchar *)printer_name)) { g_message("Printer %s added\n", (char *)printer_name); dest = (cups_dest_t *)value; send_printer_added_signal(b, dialog_name, dest); add_printer_to_dialog(b, dialog_name, dest); } } } gboolean get_hide_remote(BackendObj *b, char *dialog_name) { Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); return d->hide_remote; } gboolean get_hide_temp(BackendObj *b, char *dialog_name) { Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); return d->hide_temp; } void refresh_printer_list(BackendObj *b, char *dialog_name) { GHashTable *new_printers; new_printers = cups_get_printers(get_hide_temp(b, dialog_name), get_hide_remote(b, dialog_name)); notify_removed_printers(b, dialog_name, new_printers); notify_added_printers(b, dialog_name, new_printers); } GHashTable *get_dialog_printers(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if (d == NULL) { MSG_LOG("Invalid dialog name.\n", ERR); return NULL; } return d->printers; } PrinterCUPS *get_printer_by_name(BackendObj *b, const char *dialog_name, const char *printer_name) { GHashTable *printers = get_dialog_printers(b, dialog_name); g_assert_nonnull(printers); PrinterCUPS *p = (g_hash_table_lookup(printers, printer_name)); if (p == NULL) { printf("Printer '%s' does not exist for the dialog %s.\n", printer_name, dialog_name); exit(EXIT_FAILURE); } return p; } cups_dest_t *get_dest_by_name(BackendObj *b, const char *dialog_name, const char *printer_name) { GHashTable *printers = get_dialog_printers(b, dialog_name); g_assert_nonnull(printers); PrinterCUPS *p = (g_hash_table_lookup(printers, printer_name)); if (p == NULL) { printf("Printer '%s' does not exist for the dialog %s.\n", printer_name, dialog_name); } return p->dest; } GVariant *get_all_jobs(BackendObj *b, const char *dialog_name, int *num_jobs, gboolean active_only) { int CUPS_JOB_FLAG; if (active_only) CUPS_JOB_FLAG = CUPS_WHICHJOBS_ACTIVE; else CUPS_JOB_FLAG = CUPS_WHICHJOBS_ALL; GHashTable *printers = get_dialog_printers(b, dialog_name); GVariantBuilder *builder; GVariant *variant; builder = g_variant_builder_new(G_VARIANT_TYPE(JOB_ARRAY_ARGS)); GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, printers); int ncurr = 0; int n = 0; int num_printers = g_hash_table_size(printers); cups_job_t **jobs = g_new(cups_job_t *, num_printers); int i_printer = 0; while (g_hash_table_iter_next(&iter, &key, &value)) { /** iterate over all the printers of this dialog **/ PrinterCUPS *p = (PrinterCUPS *)value; ensure_printer_connection(p); printf(" .. %s ..", p->name); /**This is NOT reporting jobs for ipp printers : Probably a bug in cupsGetJobs2:(( **/ ncurr = cupsGetJobs2(p->http, &(jobs[i_printer]), p->name, 0, CUPS_JOB_FLAG); printf("%d\n", ncurr); n += ncurr; for (int i = 0; i < ncurr; i++) { printf("i = %d\n", i); printf("%d %s\n", jobs[i_printer][i].id, jobs[i_printer][i].title); g_variant_builder_add_value(builder, pack_cups_job(jobs[i_printer][i])); } cupsFreeJobs(ncurr, jobs[i_printer]); i_printer++; } free(jobs); *num_jobs = n; variant = g_variant_new(JOB_ARRAY_ARGS, builder); return variant; } /***************************PrinterObj********************************/ PrinterCUPS *get_new_PrinterCUPS(const cups_dest_t *dest) { PrinterCUPS *p = (PrinterCUPS *)(malloc(sizeof(PrinterCUPS))); /** Make a copy of dest, because there are no guarantees * whether dest will always exist or if it will be freed**/ cups_dest_t *dest_copy = NULL; cupsCopyDest((cups_dest_t *)dest, 0, &dest_copy); if (dest_copy == NULL) { MSG_LOG("Error creating PrinterCUPS", WARN); return NULL; } p->dest = dest_copy; p->name = dest_copy->name; p->http = NULL; p->dinfo = NULL; return p; } void free_PrinterCUPS(PrinterCUPS *p) { printf("Freeing printerCUPS \n"); cupsFreeDests(1, p->dest); if (p->dinfo) { cupsFreeDestInfo(p->dinfo); } } gboolean ensure_printer_connection(PrinterCUPS *p) { if (p->http) return TRUE; p->http = cupsConnectDest(p->dest, CUPS_DEST_FLAGS_NONE, 300, NULL, NULL, 0, NULL, NULL); if (p->http == NULL) return FALSE; p->dinfo = cupsCopyDestInfo(p->http, p->dest); if (p->dinfo == NULL) return FALSE; return TRUE; } int get_supported(PrinterCUPS *p, char ***supported_values, const char *option_name) { char **values; ensure_printer_connection(p); ipp_attribute_t *attrs = cupsFindDestSupported(p->http, p->dest, p->dinfo, option_name); int i, count = ippGetCount(attrs); if (!count) { *supported_values = NULL; return 0; } values = malloc(sizeof(char *) * count); for (i = 0; i < count; i++) { values[i] = extract_ipp_attribute(attrs, i, option_name); } *supported_values = values; return count; } char *get_orientation_default(PrinterCUPS *p) { const char *def_value = cupsGetOption(CUPS_ORIENTATION, p->dest->num_options, p->dest->options); if (def_value) { switch (def_value[0]) { case '0': return get_string_copy("automatic-rotation"); default: return get_string_copy(ippEnumString(CUPS_ORIENTATION, atoi(def_value))); } } ensure_printer_connection(p); ipp_attribute_t *attr = NULL; attr = cupsFindDestDefault(p->http, p->dest, p->dinfo, CUPS_ORIENTATION); if (!attr) return get_string_copy("NA"); const char *str = ippEnumString(CUPS_ORIENTATION, ippGetInteger(attr, 0)); if (strcmp("0", str) == 0) str = "automatic-rotation"; return get_string_copy(str); } int get_job_creation_attributes(PrinterCUPS *p, char ***values) { return get_supported(p, values, "job-creation-attributes"); } char *get_default(PrinterCUPS *p, char *option_name) { /** first take care of special cases**/ if (strcmp(option_name, CUPS_ORIENTATION) == 0) return get_orientation_default(p); /** Generic cases next **/ ensure_printer_connection(p); ipp_attribute_t *def_attr = cupsFindDestDefault(p->http, p->dest, p->dinfo, option_name); const char *def_value = cupsGetOption(option_name, p->dest->num_options, p->dest->options); /** First check the option is already there in p->dest->options **/ if (def_value) { if (def_attr && (ippGetValueTag(def_attr) == IPP_TAG_ENUM)) return get_string_copy(ippEnumString(option_name, atoi(def_value))); return get_string_copy(def_value); } if (def_attr) { return extract_ipp_attribute(def_attr, 0, option_name); } return get_string_copy("NA"); } /**************Option************************************/ Option *get_NA_option() { Option *o = (Option *)malloc(sizeof(Option)); o->option_name = "NA"; o->default_value = "NA"; o->num_supported = 0; o->supported_values = new_cstring_array(1); o->supported_values[0] = "bub"; return o; } void print_option(const Option *opt) { g_message("%s", opt->option_name); int i; for (i = 0; i < opt->num_supported; i++) { printf(" %s\n", opt->supported_values[i]); } printf("****DEFAULT: %s\n", opt->default_value); } void free_options(int count, Option *opts) { int i, j; /**Looping variables */ for (i = 0; i < count; i++) { free(opts[i].option_name); for (j = 0; j < opts[i].num_supported; j++) { free(opts[i].supported_values[j]); } free(opts[i].supported_values); free(opts[i].default_value); } free(opts); } void unpack_option_array(GVariant *var, int num_options, Option **options) { Option *opt = (Option *)(malloc(sizeof(Option) * num_options)); int i, j; char *str; GVariantIter *iter; GVariantIter *array_iter; char *name, *default_val; int num_sup; g_variant_get(var, "a(ssia(s))", &iter); for (i = 0; i < num_options; i++) { //printf("i = %d\n", i); g_variant_iter_loop(iter, "(ssia(s))", &name, &default_val, &num_sup, &array_iter); opt[i].option_name = get_string_copy(name); opt[i].default_value = get_string_copy(default_val); opt[i].num_supported = num_sup; opt[i].supported_values = new_cstring_array(num_sup); for (j = 0; j < num_sup; j++) { g_variant_iter_loop(array_iter, "(s)", &str); opt[i].supported_values[j] = get_string_copy(str); //mem } print_option(&opt[i]); } *options = opt; } GVariant *pack_option(const Option *opt) { GVariant **t = g_new(GVariant *, 4); t[0] = g_variant_new_string(opt->option_name); t[1] = g_variant_new_string(opt->default_value); //("s", get_string_copy(opt->default_value)); t[2] = g_variant_new_int32(opt->num_supported); t[3] = pack_string_array(opt->num_supported, opt->supported_values); GVariant *tuple_variant = g_variant_new_tuple(t, 4); g_free(t); return tuple_variant; } int get_all_options(PrinterCUPS *p, Option **options) { ensure_printer_connection(p); char **option_names; int num_options = get_job_creation_attributes(p, &option_names); /** number of options to be returned**/ int i, j; /**Looping variables **/ Option *opts = (Option *)(malloc(sizeof(Option) * num_options)); /**Option array, which will be filled **/ ipp_attribute_t *vals; /** Variable to store the values of the options **/ for (i = 0; i < num_options; i++) { opts[i].option_name = option_names[i]; vals = cupsFindDestSupported(p->http, p->dest, p->dinfo, option_names[i]); if (vals) opts[i].num_supported = ippGetCount(vals); else opts[i].num_supported = 0; /** Retreive all the supported values for that option **/ opts[i].supported_values = new_cstring_array(opts[i].num_supported); for (j = 0; j < opts[i].num_supported; j++) { opts[i].supported_values[j] = extract_ipp_attribute(vals, j, option_names[i]); if (opts[i].supported_values[j] == NULL) { opts[i].supported_values[j] = get_string_copy("NA"); } } /** Retrieve the default value for that option **/ opts[i].default_value = get_default(p, option_names[i]); if (opts[i].default_value == NULL) { opts[i].default_value = get_string_copy("NA"); } } *options = opts; return num_options; } const char *get_printer_state(PrinterCUPS *p) { const char *str; ensure_printer_connection(p); ipp_t *request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); const char *uri = cupsGetOption("printer-uri-supported", p->dest->num_options, p->dest->options); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); const char *const requested_attributes[] = {"printer-state"}; ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", 1, NULL, requested_attributes); ipp_t *response = cupsDoRequest(p->http, request, "/"); if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST) { /* request failed */ printf("Request failed: %s\n", cupsLastErrorString()); return "NA"; } ipp_attribute_t *attr; if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) { printf("printer-state=%d\n", ippGetInteger(attr, 0)); str = map->state[ippGetInteger(attr, 0)]; } return str; } int print_file(PrinterCUPS *p, const char *file_path, int num_settings, GVariant *settings) { ensure_printer_connection(p); int num_options = 0; cups_option_t *options; GVariantIter *iter; g_variant_get(settings, "a(ss)", &iter); int i = 0; char *option_name, *option_value; for (i = 0; i < num_settings; i++) { g_variant_iter_loop(iter, "(ss)", &option_name, &option_value); printf(" %s : %s\n", option_name, option_value); /** * to do: * instead of directly adding the option,convert it from the frontend's lingo * to the specific lingo of the backend * * use PWG names instead */ num_options = cupsAddOption(option_name, option_value, num_options, &options); } char *file_name = extract_file_name(file_path); int job_id = 0; cupsCreateDestJob(p->http, p->dest, p->dinfo, &job_id, file_name, num_options, options); if (job_id) { /** job creation was successful , * Now let's submit a document * and start writing data onto it **/ printf("Created job %d\n", job_id); http_status_t http_status; /**document creation status **/ http_status = cupsStartDestDocument(p->http, p->dest, p->dinfo, job_id, file_name, CUPS_FORMAT_AUTO, num_options, options, 1); if (http_status == HTTP_STATUS_CONTINUE) { /**Document submitted successfully; * Now write the data onto it**/ FILE *fp = fopen(file_path, "rb"); size_t bytes; char buffer[65536]; /** Read and write the data chunk by chunk **/ while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { http_status = cupsWriteRequestData(p->http, buffer, bytes); if (http_status != HTTP_STATUS_CONTINUE) { printf("Error writing print data to server.\n"); break; } } if (cupsFinishDestDocument(p->http, p->dest, p->dinfo) == IPP_STATUS_OK) printf("Document send succeeded.\n"); else printf("Document send failed: %s\n", cupsLastErrorString()); fclose(fp); return job_id; /**some correction needed here **/ } } else { printf("Unable to create job %s\n", cupsLastErrorString()); return 0; } } int get_active_jobs_count(PrinterCUPS *p) { ensure_printer_connection(p); cups_job_t *jobs; int num_jobs = cupsGetJobs2(p->http, &jobs, p->name, 0, CUPS_WHICHJOBS_ACTIVE); cupsFreeJobs(num_jobs, jobs); return num_jobs; } gboolean cancel_job(PrinterCUPS *p, int jobid) { ensure_printer_connection(p); ipp_status_t status = cupsCancelDestJob(p->http, p->dest, jobid); if (status == IPP_STATUS_OK) return TRUE; return FALSE; } void printAllJobs(PrinterCUPS *p) { ensure_printer_connection(p); cups_job_t *jobs; int num_jobs = cupsGetJobs2(p->http, &jobs, p->name, 1, CUPS_WHICHJOBS_ALL); for (int i = 0; i < num_jobs; i++) { print_job(&jobs[i]); } } static void list_group(ppd_file_t *ppd, /* I - PPD file */ ppd_group_t *group) /* I - Group to show */ { printf("List group %s\n", group->name); /** Now iterate through the options in the particular group*/ printf("It has %d options.\n", group->num_options); printf("Listing all of them ..\n"); int i; for (i = 0; i < group->num_options; i++) { printf(" Option %d : %s\n", i, group->options[i].keyword); } } void tryPPD(PrinterCUPS *p) { const char *filename; /* PPD filename */ ppd_file_t *ppd; /* PPD data */ ppd_group_t *group; /* Current group */ if ((filename = cupsGetPPD(p->dest->name)) == NULL) { printf("Error getting ppd file.\n"); return; } g_message("Got ppd file %s\n", filename); if ((ppd = ppdOpenFile(filename)) == NULL) { printf("Error opening ppd file.\n"); return; } printf("Opened ppd file.\n"); ppdMarkDefaults(ppd); cupsMarkOptions(ppd, p->dest->num_options, p->dest->options); group = ppd->groups; for (int i = ppd->num_groups; i > 0; i--) { /**iterate through all the groups in the ppd file */ list_group(ppd, group); group++; } } /**********Dialog related funtions ****************/ Dialog *get_new_Dialog() { Dialog *d = g_new(Dialog, 1); d->cancel = 0; d->hide_remote = FALSE; d->hide_temp = FALSE; d->keep_alive = FALSE; d->printers = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)free_string, (GDestroyNotify)free_PrinterCUPS); return d; } void free_Dialog(Dialog *d) { printf("freeing dialog..\n"); g_hash_table_destroy(d->printers); free(d); } /*********Mappings********/ Mappings *get_new_Mappings() { Mappings *m = (Mappings *)(malloc(sizeof(Mappings))); m->state[3] = STATE_IDLE; m->state[4] = STATE_PRINTING; m->state[5] = STATE_STOPPED; m->orientation[atoi(CUPS_ORIENTATION_LANDSCAPE)] = ORIENTATION_LANDSCAPE; m->orientation[atoi(CUPS_ORIENTATION_PORTRAIT)] = ORIENTATION_PORTRAIT; return m; } /*****************CUPS and IPP helpers*********************/ const char *cups_printer_state(cups_dest_t *dest) { //cups_dest_t *dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer_name, NULL); g_assert_nonnull(dest); const char *state = cupsGetOption("printer-state", dest->num_options, dest->options); if (state == NULL) return "NA"; return map->state[state[0] - '0']; } gboolean cups_is_accepting_jobs(cups_dest_t *dest) { g_assert_nonnull(dest); const char *val = cupsGetOption("printer-is-accepting-jobs", dest->num_options, dest->options); return get_boolean(val); } void cups_get_Resolution(cups_dest_t *dest, int *xres, int *yres) { http_t *http = cupsConnectDest(dest, CUPS_DEST_FLAGS_NONE, 500, NULL, NULL, 0, NULL, NULL); g_assert_nonnull(http); cups_dinfo_t *dinfo = cupsCopyDestInfo(http, dest); g_assert_nonnull(dinfo); ipp_attribute_t *attr = cupsFindDestDefault(http, dest, dinfo, "printer-resolution"); ipp_res_t *units; *xres = ippGetResolution(attr, 0, yres, units); } int add_printer_to_ht(void *user_data, unsigned flags, cups_dest_t *dest) { GHashTable *h = (GHashTable *)user_data; char *printername = get_string_copy(dest->name); cups_dest_t *dest_copy = NULL; cupsCopyDest(dest, 0, &dest_copy); g_hash_table_insert(h, printername, dest_copy); return 1; } int add_printer_to_ht_no_temp(void *user_data, unsigned flags, cups_dest_t *dest) { if (cups_is_temporary(dest)) return 1; GHashTable *h = (GHashTable *)user_data; char *printername = get_string_copy(dest->name); cups_dest_t *dest_copy = NULL; cupsCopyDest(dest, 0, &dest_copy); g_hash_table_insert(h, printername, dest_copy); return 1; } GHashTable *cups_get_printers(gboolean notemp, gboolean noremote) { cups_dest_cb_t cb = add_printer_to_ht; unsigned type = 0, mask = 0; if (noremote) { type = CUPS_PRINTER_LOCAL; mask = CUPS_PRINTER_REMOTE; } if (notemp) { cb = add_printer_to_ht_no_temp; } GHashTable *printers_ht = g_hash_table_new(g_str_hash, g_str_equal); cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, //timeout NULL, //cancel type, //TYPE mask, //MASK cb, //function printers_ht); //user_data return printers_ht; } GHashTable *cups_get_all_printers() { printf("all printers\n"); // to do : fix GHashTable *printers_ht = g_hash_table_new(g_str_hash, g_str_equal); cupsEnumDests(CUPS_DEST_FLAGS_NONE, 3000, //timeout NULL, //cancel 0, //TYPE 0, //MASK add_printer_to_ht, //function printers_ht); //user_data return printers_ht; } GHashTable *cups_get_local_printers() { printf("local printers\n"); //to do: fix GHashTable *printers_ht = g_hash_table_new(g_str_hash, g_str_equal); cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1200, //timeout NULL, //cancel CUPS_PRINTER_LOCAL, //TYPE CUPS_PRINTER_REMOTE, //MASK add_printer_to_ht, //function printers_ht); //user_data return printers_ht; } char *cups_retrieve_string(cups_dest_t *dest, const char *option_name) { /** this funtion is kind of a wrapper , to ensure that the return value is never NULL , as that can cause the backend to segFault **/ g_assert_nonnull(dest); g_assert_nonnull(option_name); char *ans = NULL; ans = get_string_copy(cupsGetOption(option_name, dest->num_options, dest->options)); if (ans) return ans; return get_string_copy("NA"); } gboolean cups_is_temporary(cups_dest_t *dest) { g_assert_nonnull(dest); if (cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) return FALSE; return TRUE; } char *extract_ipp_attribute(ipp_attribute_t *attr, int index, const char *option_name) { /** first deal with the totally unique cases **/ if (strcmp(option_name, CUPS_ORIENTATION) == 0) return extract_orientation_from_ipp(attr, index); /** Then deal with the generic cases **/ char *str; const char *attrstr; switch (ippGetValueTag(attr)) { case IPP_TAG_INTEGER: str = (char *)(malloc(sizeof(char) * 50)); snprintf(str, sizeof(str), "%d", ippGetInteger(attr, index)); break; case IPP_TAG_ENUM: attrstr = ippEnumString(option_name, ippGetInteger(attr, index)); str = strdup(attrstr); break; case IPP_TAG_RANGE: str = (char *)(malloc(sizeof(char) * 100)); int upper, lower = ippGetRange(attr, index, &upper); snprintf(str, sizeof(str), "%d-%d", lower, upper); break; case IPP_TAG_RESOLUTION: return extract_res_from_ipp(attr, index); default: return extract_string_from_ipp(attr, index); } return str; } char *extract_res_from_ipp(ipp_attribute_t *attr, int index) { int xres, yres; ipp_res_t units; xres = ippGetResolution(attr, index, &yres, &units); char *unit = units == IPP_RES_PER_INCH ? "dpi" : "dpcm"; char buf[100]; if (xres == yres) snprintf(buf, sizeof(buf), "%d%s", xres, unit); else snprintf(buf, sizeof(buf), "%dx%d%s", xres, yres, unit); return get_string_copy(buf); } char *extract_string_from_ipp(ipp_attribute_t *attr, int index) { return get_string_copy(ippGetString(attr, index, NULL)); } char *extract_orientation_from_ipp(ipp_attribute_t *attr, int index) { char *str = get_string_copy(ippEnumString(CUPS_ORIENTATION, ippGetInteger(attr, index))); if (strcmp("0", str) == 0) str = get_string_copy("automatic-rotation"); return str; } void print_job(cups_job_t *j) { printf("title : %s\n", j->title); printf("dest : %s\n", j->dest); printf("job-id : %d\n", j->id); printf("user : %s\n", j->user); char *state = translate_job_state(j->state); printf("state : %s\n", state); } char *translate_job_state(ipp_jstate_t state) { switch (state) { case IPP_JSTATE_ABORTED: return JOB_STATE_ABORTED; case IPP_JSTATE_CANCELED: return JOB_STATE_CANCELLED; case IPP_JSTATE_HELD: return JOB_STATE_HELD; case IPP_JSTATE_PENDING: return JOB_STATE_PENDING; case IPP_JSTATE_PROCESSING: return JOB_STATE_PRINTING; case IPP_JSTATE_STOPPED: return JOB_STATE_STOPPED; case IPP_JSTATE_COMPLETED: return JOB_STATE_COMPLETED; default: return "NA"; } } GVariant *pack_cups_job(cups_job_t job) { printf("%s\n", job.dest); GVariant **t = g_new0(GVariant *, 7); char jobid[20]; snprintf(jobid, sizeof(jobid), "%d", job.id); t[0] = g_variant_new_string(jobid); t[1] = g_variant_new_string(job.title); t[2] = g_variant_new_string(job.dest); t[3] = g_variant_new_string(job.user); t[4] = g_variant_new_string(translate_job_state(job.state)); t[5] = g_variant_new_string(httpGetDateString(job.creation_time)); t[6] = g_variant_new_int32(job.size); GVariant *tuple_variant = g_variant_new_tuple(t, 7); g_free(t); return tuple_variant; } void MSG_LOG(const char *msg, int msg_level) { if (MSG_LOG_LEVEL >= msg_level) { printf("%s\n", msg); fflush(stdout); } } void free_string(char *str) { if (str) { free(str); } } cpdb-backend-cups-1.1.1/src/backend_helper.h000066400000000000000000000164741353216073400206650ustar00rootroot00000000000000#ifndef _BACKEND_HELPER_H #define _BACKEND_HELPER_H #include #include #include #include #include #include #include #define INFO 3 #define WARN 2 #define ERR 1 #define MSG_LOG_LEVEL INFO /** * Represents a CUPS Printer */ typedef struct _PrinterCUPS { char *name; cups_dest_t *dest; http_t *http; cups_dinfo_t *dinfo; } PrinterCUPS; /** * Represents a frontend instance that the backend is associated with */ typedef struct _Dialog { int cancel; gboolean hide_remote; gboolean hide_temp; GHashTable *printers; gboolean keep_alive; } Dialog; typedef struct _Mappings { GHashTable *media; GHashTable *color; GHashTable *print_quality; const char *orientation[10]; const char *state[6]; } Mappings; /** * Represents the CUPS Backend */ typedef struct _BackendObj { GDBusConnection *dbus_connection; PrintBackend *skeleton; char *obj_path; /** the hash table to map from dialog name(char*) to the Dialog struct(Dialog*) **/ GHashTable *dialogs; int num_frontends; char *default_printer; } BackendObj; /** * Represents a single 'option' for a printer */ typedef struct _Option { char *option_name; int num_supported; char **supported_values; char *default_value; } Option; /********Backend related functions*******************/ /** Get a new BackendObj **/ BackendObj *get_new_BackendObj(); /** Get the printer-id of the default printer of the CUPS Backend**/ char *get_default_printer(BackendObj *b); /** Connect the BackendObj to the dbus **/ void connect_to_dbus(BackendObj *, char *obj_path); /** Add the dialog to the list of dialogs of the particular backend**/ void add_frontend(BackendObj *, const char *dialog_name); /** Remove the dialog from the list of frontends that this backend is * associated with. */ void remove_frontend(BackendObj *, const char *dialog_name); /** Checks if the backend isn't associated with any frontend **/ gboolean no_frontends(BackendObj *); /** * Find the dialog with the specified name */ Dialog* find_dialog(BackendObj * , const char* dialog_name); /** Get the variable which controls the cancellation of the enumeration thread for * that particular dialog */ int *get_dialog_cancel(BackendObj *, const char *dialog_name); void set_dialog_cancel(BackendObj *, const char *dialog_name); //make cancel = 0 void reset_dialog_cancel(BackendObj *, const char *dialog_name); //make cancel = 1 /** Returns whether remote CUPS printers are hidden for this dialog **/ gboolean get_hide_remote(BackendObj *b, char *dialog_name); /** Hides remote CUPS printers for the dialog **/ void set_hide_remote_printers(BackendObj *, const char *dialog_name); /** Unhides remote CUPS Printers for the dialog **/ void unset_hide_remote_printers(BackendObj *, const char *dialog_name); /** Returns whether temporary CUPS Queues(discovered, but not set up) are hidden for this dialog **/ gboolean get_hide_temp(BackendObj *b, char *dialog_name); /** Hides temporary CUPS queues for the dialog **/ void set_hide_temp_printers(BackendObj *, const char *dialog_name); /** Unhides temporary CUPS queues for the dialog **/ void unset_hide_temp_printers(BackendObj *, const char *dialog_name); /** * Returns * TRUE if the printer with specified name is found for the dialog * FALSE otherwise */ gboolean dialog_contains_printer(BackendObj *, const char *dialog_name, const char *printer_name); /** * Adds the corresponding CUPS printer to the dialog's printer list * * Returns * the PrinterCUPS* struct added to the dialog * NULL if the operation was unsuccesful */ PrinterCUPS *add_printer_to_dialog(BackendObj *, const char *dialog_name, const cups_dest_t *dest); /** * Removes the printer with the specified name from the dialog's list of printers */ void remove_printer_from_dialog(BackendObj *, const char *dialog_name, const char *printer_name); void send_printer_added_signal(BackendObj *b, const char *dialog_name, cups_dest_t *dest); void send_printer_removed_signal(BackendObj *b, const char *dialog_name, const char *printer_name); void notify_removed_printers(BackendObj *b, const char *dialog_name, GHashTable *new_table); void notify_added_printers(BackendObj *b, const char *dialog_name, GHashTable *new_table); void replace_printers(BackendObj *b, const char *dialog_name, GHashTable *new_table); void refresh_printer_list(BackendObj *b, char *dialog_name); GHashTable *get_dialog_printers(BackendObj *b, const char *dialog_name); cups_dest_t *get_dest_by_name(BackendObj *b, const char *dialog_name, const char *printer_name); PrinterCUPS *get_printer_by_name(BackendObj *b, const char *dialog_name, const char *printer_name); GVariant *get_all_jobs(BackendObj *b, const char *dialog_name, int *num_jobs, gboolean active_only); /*********Printer related functions******************/ /** Get a new PrinterCUPS struct associated with the cups destination**/ PrinterCUPS *get_new_PrinterCUPS(const cups_dest_t *dest); /** Free up the memory used by the struct **/ void free_PrinterCUPS(PrinterCUPS *); /** Ensure that we have a connection the server**/ gboolean ensure_printer_connection(PrinterCUPS *p); /** * Get state of the printer * state is one of the following {"idle" , "processing" , "stopped"} */ const char *get_printer_state(PrinterCUPS *p); char *get_orientation_default(PrinterCUPS *p); char *get_default(PrinterCUPS *p, char *option_name); int get_supported(PrinterCUPS *p, char ***supported_values, const char *option_name); int get_job_creation_attributes(PrinterCUPS *p, char ***values); int get_all_options(PrinterCUPS *p, Option **options); int print_file(PrinterCUPS *p, const char *file_path, int num_settings, GVariant *settings); int get_active_jobs_count(PrinterCUPS *p); gboolean cancel_job(PrinterCUPS *p, int jobid); void tryPPD(PrinterCUPS *p); /**********Dialog related funtions ****************/ Dialog *get_new_Dialog(); void free_Dialog(Dialog *); /*********Option related functions*****************/ void print_option(const Option *opt); void free_options(int count, Option *opts); void unpack_option_array(GVariant *var, int num_options, Option **options); GVariant *pack_option(const Option *opt); /**********Mapping related functions*****************/ Mappings *get_new_Mappings(); /*************CUPS/IPP RELATED FUNCTIONS******************/ const char *cups_printer_state(cups_dest_t *dest); gboolean cups_is_accepting_jobs(cups_dest_t *dest); void cups_get_Resolution(cups_dest_t *dest, int *xres, int *yres); GHashTable *cups_get_all_printers(); GHashTable *cups_get_local_printers(); char *cups_retrieve_string(cups_dest_t *dest, const char *option_name); gboolean cups_is_temporary(cups_dest_t *dest); GHashTable *cups_get_printers(gboolean notemp, gboolean noremote); char *extract_ipp_attribute(ipp_attribute_t *, int index, const char *option_name); char *extract_res_from_ipp(ipp_attribute_t *, int index); char *extract_string_from_ipp(ipp_attribute_t *attr, int index); char *extract_orientation_from_ipp(ipp_attribute_t *attr, int index); void print_job(cups_job_t *j); GVariant *pack_cups_job(cups_job_t job); char *translate_job_state(ipp_jstate_t); /***************Misc.** **********************/ /**error logging */ void MSG_LOG(const char *msg, int msg_level); void free_string(char *); void free_string_array(int count, char **arr); #endif cpdb-backend-cups-1.1.1/src/print_backend_cups.c000066400000000000000000000545621353216073400215670ustar00rootroot00000000000000#include #include #include #include #include #include #include "backend_helper.h" #define _CUPS_NO_DEPRECATED 1 #define BUS_NAME "org.openprinting.Backend.CUPS" #define OBJECT_PATH "/" static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer not_used); static void acquire_session_bus_name(); gpointer list_printers(gpointer _dialog_name); int send_printer_added(void *_dialog_name, unsigned flags, cups_dest_t *dest); void connect_to_signals(); BackendObj *b; int main() { /* Initialize internal default settings of the CUPS library */ int p = ippPort(); b = get_new_BackendObj(); acquire_session_bus_name(BUS_NAME); GMainLoop *loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(loop); } static void acquire_session_bus_name(char *bus_name) { g_bus_own_name(G_BUS_TYPE_SESSION, bus_name, 0, //flags NULL, //bus_acquired_handler on_name_acquired, //name acquired handler NULL, //name_lost handler NULL, //user_data NULL); //user_data free function } static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer not_used) { b->dbus_connection = connection; b->skeleton = print_backend_skeleton_new(); connect_to_signals(); connect_to_dbus(b, OBJECT_PATH); } static gboolean on_handle_activate_backend(PrintBackend *interface, GDBusMethodInvocation *invocation, gpointer not_used) { /** This function starts the backend and starts sending the printers **/ const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); /// potential risk add_frontend(b, dialog_name); g_thread_new(NULL, list_printers, (gpointer)dialog_name); return TRUE; } gpointer list_printers(gpointer _dialog_name) { char *dialog_name = (char *)_dialog_name; g_message("New thread for dialog at %s\n", dialog_name); int *cancel = get_dialog_cancel(b, dialog_name); cupsEnumDests(CUPS_DEST_FLAGS_NONE, -1, //NO timeout cancel, 0, //TYPE 0, //MASK send_printer_added, _dialog_name); g_message("Exiting thread for dialog at %s\n", dialog_name); return NULL; } int send_printer_added(void *_dialog_name, unsigned flags, cups_dest_t *dest) { char *dialog_name = (char *)_dialog_name; char *printer_name = dest->name; if (dialog_contains_printer(b, dialog_name, printer_name)) { g_message("%s already sent.\n", printer_name); return 1; } add_printer_to_dialog(b, dialog_name, dest); send_printer_added_signal(b, dialog_name, dest); g_message(" Sent notification for printer %s\n", printer_name); /** dest will be automatically freed later. * Don't explicitly free it */ return 1; //continue enumeration } static void on_stop_backend(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer not_used) { g_message("Stop backend signal from %s\n", sender_name); /** * Ignore this signal if the dialog's keep_alive variable is set */ Dialog *d = find_dialog(b, sender_name); if (d->keep_alive) return; set_dialog_cancel(b, sender_name); remove_frontend(b, sender_name); if (no_frontends(b)) { g_message("No frontends connected .. exiting backend.\n"); exit(EXIT_SUCCESS); } } static void on_refresh_backend(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer not_used) { char *dialog_name = get_string_copy(sender_name); g_message("Refresh backend signal from %s\n", dialog_name); set_dialog_cancel(b, dialog_name); /// this stops the enumeration of printers refresh_printer_list(b, dialog_name); } static void on_hide_remote_printers(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer not_used) { char *dialog_name = get_string_copy(sender_name); g_message("%s signal from %s\n", HIDE_REMOTE_CUPS_SIGNAL, dialog_name); if (!get_hide_remote(b, dialog_name)) { set_dialog_cancel(b, dialog_name); set_hide_remote_printers(b, dialog_name); refresh_printer_list(b, dialog_name); } } static void on_unhide_remote_printers(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer not_used) { char *dialog_name = get_string_copy(sender_name); g_message("%s signal from %s\n", UNHIDE_REMOTE_CUPS_SIGNAL, dialog_name); if (get_hide_remote(b, dialog_name)) { set_dialog_cancel(b, dialog_name); unset_hide_remote_printers(b, dialog_name); refresh_printer_list(b, dialog_name); } } static void on_hide_temp_printers(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer not_used) { char *dialog_name = get_string_copy(sender_name); g_message("%s signal from %s\n", HIDE_TEMP_CUPS_SIGNAL, dialog_name); if (!get_hide_temp(b, dialog_name)) { set_dialog_cancel(b, dialog_name); set_hide_temp_printers(b, dialog_name); refresh_printer_list(b, dialog_name); } } static void on_unhide_temp_printers(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer not_used) { char *dialog_name = get_string_copy(sender_name); g_message("%s signal from %s\n", UNHIDE_TEMP_CUPS_SIGNAL, dialog_name); if (get_hide_temp(b, dialog_name)) { set_dialog_cancel(b, dialog_name); unset_hide_temp_printers(b, dialog_name); refresh_printer_list(b, dialog_name); } } static gboolean on_handle_is_accepting_jobs(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); cups_dest_t *dest = get_dest_by_name(b, dialog_name, printer_name); g_assert_nonnull(dest); print_backend_complete_is_accepting_jobs(interface, invocation, cups_is_accepting_jobs(dest)); return TRUE; } static gboolean on_handle_get_printer_state(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); /// potential risk PrinterCUPS *p = get_printer_by_name(b, dialog_name, printer_name); const char *state = get_printer_state(p); printf("%s is %s\n", printer_name, state); print_backend_complete_get_printer_state(interface, invocation, state); return TRUE; } static gboolean on_handle_ping(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); /// potential risk PrinterCUPS *p = get_printer_by_name(b, dialog_name, printer_name); print_backend_complete_ping(interface, invocation); tryPPD(p); return TRUE; } static gboolean on_handle_print_file(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, const gchar *file_path, int num_settings, GVariant *settings, const gchar *final_file_path, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); /// potential risk PrinterCUPS *p = get_printer_by_name(b, dialog_name, printer_name); int job_id = print_file(p, file_path, num_settings, settings); char jobid_string[64]; snprintf(jobid_string, sizeof(jobid_string), "%d", job_id); print_backend_complete_print_file(interface, invocation, jobid_string); /** * (Currently Disabled) Printing will always be the last operation, so remove that frontend */ //set_dialog_cancel(b, dialog_name); //remove_frontend(b, dialog_name); if (no_frontends(b)) { g_message("No frontends connected .. exiting backend.\n"); exit(EXIT_SUCCESS); } return TRUE; } static gboolean on_handle_get_all_options(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); /// potential risk PrinterCUPS *p = get_printer_by_name(b, dialog_name, printer_name); Option *options; int count = get_all_options(p, &options); GVariantBuilder *builder; GVariant *variant; builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssia(s))")); for (int i = 0; i < count; i++) { GVariant *tuple = pack_option(&options[i]); g_variant_builder_add_value(builder, tuple); //g_variant_unref(tuple); } variant = g_variant_builder_end(builder); print_backend_complete_get_all_options(interface, invocation, count, variant); free_options(count, options); return TRUE; } static gboolean on_handle_get_active_jobs_count(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); /// potential risk PrinterCUPS *p = get_printer_by_name(b, dialog_name, printer_name); print_backend_complete_get_active_jobs_count(interface, invocation, get_active_jobs_count(p)); return TRUE; } static gboolean on_handle_get_all_jobs(PrintBackend *interface, GDBusMethodInvocation *invocation, gboolean active_only, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); /// potential risk int n; GVariant *variant = get_all_jobs(b, dialog_name, &n, active_only); print_backend_complete_get_all_jobs(interface, invocation, n, variant); return TRUE; } static gboolean on_handle_cancel_job(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *job_id, const gchar *printer_name, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); int jobid = atoi(job_id); /**to do. check if given job id is integer */ PrinterCUPS *p = get_printer_by_name(b, dialog_name, printer_name); gboolean status = cancel_job(p, jobid); print_backend_complete_cancel_job(interface, invocation, status); return TRUE; } static gboolean on_handle_get_default_printer(PrintBackend *interface, GDBusMethodInvocation *invocation, gpointer user_data) { char *def = get_default_printer(b); printf("%s\n", def); print_backend_complete_get_default_printer(interface, invocation, def); return TRUE; } static gboolean on_handle_keep_alive(PrintBackend *interface, GDBusMethodInvocation *invocation, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); Dialog *d = find_dialog(b, dialog_name); d->keep_alive = TRUE; print_backend_complete_keep_alive(interface, invocation); return TRUE; } static gboolean on_handle_replace(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *previous_name, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); Dialog *d = find_dialog(b, previous_name); if (d != NULL) { g_hash_table_steal(b->dialogs, previous_name); g_hash_table_insert(b->dialogs, get_string_copy(dialog_name), d); g_message("Replaced %s --> %s\n", previous_name, dialog_name); } print_backend_complete_replace(interface, invocation); return TRUE; } void connect_to_signals() { PrintBackend *skeleton = b->skeleton; g_signal_connect(skeleton, //instance "handle-activate-backend", //signal name G_CALLBACK(on_handle_activate_backend), //callback NULL); g_signal_connect(skeleton, //instance "handle-get-all-options", //signal name G_CALLBACK(on_handle_get_all_options), //callback NULL); g_signal_connect(skeleton, //instance "handle-ping", //signal name G_CALLBACK(on_handle_ping), //callback NULL); g_signal_connect(skeleton, //instance "handle-get-default-printer", //signal name G_CALLBACK(on_handle_get_default_printer), //callback NULL); g_signal_connect(skeleton, //instance "handle-print-file", //signal name G_CALLBACK(on_handle_print_file), //callback NULL); g_signal_connect(skeleton, //instance "handle-get-printer-state", //signal name G_CALLBACK(on_handle_get_printer_state), //callback NULL); g_signal_connect(skeleton, //instance "handle-is-accepting-jobs", //signal name G_CALLBACK(on_handle_is_accepting_jobs), //callback NULL); g_signal_connect(skeleton, //instance "handle-get-active-jobs-count", //signal name G_CALLBACK(on_handle_get_active_jobs_count), //callback NULL); g_signal_connect(skeleton, //instance "handle-get-all-jobs", //signal name G_CALLBACK(on_handle_get_all_jobs), //callback NULL); g_signal_connect(skeleton, //instance "handle-cancel-job", //signal name G_CALLBACK(on_handle_cancel_job), //callback NULL); g_signal_connect(skeleton, //instance "handle-keep-alive", //signal name G_CALLBACK(on_handle_keep_alive), //callback NULL); g_signal_connect(skeleton, //instance "handle-replace", //signal name G_CALLBACK(on_handle_replace), //callback NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, //Sender name "org.openprinting.PrintFrontend", //Sender interface STOP_BACKEND_SIGNAL, //Signal name NULL, /**match on all object paths**/ NULL, /**match on all arguments**/ 0, //Flags on_stop_backend, //callback NULL, //user_data NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, //Sender name "org.openprinting.PrintFrontend", //Sender interface REFRESH_BACKEND_SIGNAL, //Signal name NULL, /**match on all object paths**/ NULL, /**match on all arguments**/ 0, //Flags on_refresh_backend, //callback NULL, //user_data NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, //Sender name "org.openprinting.PrintFrontend", //Sender interface HIDE_REMOTE_CUPS_SIGNAL, //Signal name NULL, /**match on all object paths**/ NULL, /**match on all arguments**/ 0, //Flags on_hide_remote_printers, //callback NULL, //user_data NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, //Sender name "org.openprinting.PrintFrontend", //Sender interface UNHIDE_REMOTE_CUPS_SIGNAL, //Signal name NULL, /**match on all object paths**/ NULL, /**match on all arguments**/ 0, //Flags on_unhide_remote_printers, //callback NULL, //user_data NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, //Sender name "org.openprinting.PrintFrontend", //Sender interface HIDE_TEMP_CUPS_SIGNAL, //Signal name NULL, /**match on all object paths**/ NULL, /**match on all arguments**/ 0, //Flags on_hide_temp_printers, //callback NULL, //user_data NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, //Sender name "org.openprinting.PrintFrontend", //Sender interface UNHIDE_TEMP_CUPS_SIGNAL, //Signal name NULL, /**match on all object paths**/ NULL, /**match on all arguments**/ 0, //Flags on_unhide_temp_printers, //callback NULL, //user_data NULL); }