pax_global_header00006660000000000000000000000064133363251630014517gustar00rootroot0000000000000052 comment=c46c43a9d7add36360a51406eea6db154dfd122b cpdb-backend-file-1.0.1/000077500000000000000000000000001333632516300147305ustar00rootroot00000000000000cpdb-backend-file-1.0.1/.gitignore000066400000000000000000000002751333632516300167240ustar00rootroot00000000000000*.out *.o *.pdf gmon.out Makefile.in aclocal.m4 Makefile depcomp install-sh missing aclocal.* compile autom4* config.* configure data/org.openprinting.Backend.FILE.service .deps *.swp file cpdb-backend-file-1.0.1/LICENSE.md000066400000000000000000000020551333632516300163360ustar00rootroot00000000000000MIT License Copyright (c) 2018 Ayush Bansal 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-file-1.0.1/Makefile.am000066400000000000000000000000561333632516300167650ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign SUBDIRS = src data cpdb-backend-file-1.0.1/README.md000066400000000000000000000024021333632516300162050ustar00rootroot00000000000000# File Common Print Dialog Backend This repository contains one of the components I worked on as part of my Google Summer of Code'18 project with the Linux Foundation, the complete documentation can be found [here](https://github.com/ayush268/GSoC_2018_Documentation). This repository hosts the code for the File **C**ommon **P**rint **D**ialog **B**ackend. This backend manages and provides information about printing to a file via 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 - GLIB 2.0: `sudo apt install libglib2.0-dev` ## Build and installation ``` $ ./autogen.sh $ ./configure $ make $ sudo make install ``` ## Following the development and updates The current source code can be found on the [My Github Repo](https://github.com/ayush268/cpdb-backend-file) ## Running The backend is auto-activated when a frontend runs (which is in cpdb-libs); So no need to run it explicitly. However, if you wish to see the debug statements in the backend code, you can run `/usr/local/lib/print-backends/file`. cpdb-backend-file-1.0.1/autogen.sh000077500000000000000000000000761333632516300167340ustar00rootroot00000000000000#! /bin/sh aclocal \ && automake --add-missing \ && autoconf cpdb-backend-file-1.0.1/configure.ac000066400000000000000000000022471333632516300172230ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) AC_INIT([cpdb-backend-file],[1.0.1],[ayushb268@gmail.com],[CPDB - FILE Backend], [https://github.com/ayush268/cpdb-backend-file]) AM_INIT_AUTOMAKE([-Wall foreign]) AC_CONFIG_SRCDIR([src/print_backend_file.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]) # 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-file-1.0.1/data/000077500000000000000000000000001333632516300156415ustar00rootroot00000000000000cpdb-backend-file-1.0.1/data/Makefile.am000066400000000000000000000006161333632516300177000ustar00rootroot00000000000000# Dbus service file servicedir = $(DBUS_SERVICES_DIR) service_in_files = org.openprinting.Backend.FILE.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.FILE clean-local: rm -rf $(service_DATA) cpdb-backend-file-1.0.1/data/org.openprinting.Backend.FILE000066400000000000000000000000021333632516300231220ustar00rootroot00000000000000/ cpdb-backend-file-1.0.1/data/org.openprinting.Backend.FILE.service.in000066400000000000000000000001201333632516300251670ustar00rootroot00000000000000[D-BUS Service] Name=org.openprinting.Backend.FILE Exec=@cpdb_backend_dir@/file cpdb-backend-file-1.0.1/src/000077500000000000000000000000001333632516300155175ustar00rootroot00000000000000cpdb-backend-file-1.0.1/src/Makefile.am000066400000000000000000000006221333632516300175530ustar00rootroot00000000000000backenddir = $(CPDB_BACKEND_DIR) backend_PROGRAMS = file file_SOURCES = print_backend_file.c backend_helper.c file_CPPFLAGS = $(CPDBBACKEND_CFLAGS) file_CPPFLAGS += $(GLIB_CFLAGS) file_CPPFLAGS += $(GIO_CFLAGS) file_CPPFLAGS += $(GIOUNIX_CFLAGS) file_LDADD = $(CPDBBACKEND_LIBS) file_LDADD += -lpthread -lm -lcrypt file_LDADD += $(GLIB_LIBS) file_LDADD += $(GIO_LIBS) file_LDADD += $(GIOUNIX_LIBS) cpdb-backend-file-1.0.1/src/backend_helper.c000066400000000000000000000106251333632516300206150ustar00rootroot00000000000000#include "backend_helper.h" /* Create new objects */ BackendObj *get_new_BackendObj() { // dbus_connection and skeleton we be created later 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; } Dialog *get_new_Dialog() { // Right now it will have only one kind of printer but multiple can be added. Dialog *d = g_new(Dialog, 1); d->cancel = 0; d->keep_alive = FALSE; d->printers = get_FilePrinter(); return d; } FilePrinter *get_FilePrinter() { // Printer for saving the file as PDF. FilePrinter *p = g_new(FilePrinter, 1); p->name = "Save_As_PDF"; p->info = "Printing to a PDF File"; p->location = "localhost"; p->state = "idle"; p->is_accepting_jobs = 1; p->num_options = 0; p->options = NULL; return p; } /* Functions used to execute various D-Bus Methods */ void add_frontend(BackendObj *b, const char *dialog_name) { Dialog *d = get_new_Dialog(); b->default_printer = d->printers->name; 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); } 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 FILE Backend to D-Bus\n", ERR); } } void send_printer_added_signal(BackendObj *b, const char *dialog_name) { Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if (d == NULL) { MSG_LOG("Failed to retrieve dialog for FILE Backend.\n", ERR); exit(EXIT_FAILURE); } GVariant *gv = g_variant_new(PRINTER_ADDED_ARGS, d->printers->name, d->printers->name, d->printers->info, d->printers->location, "", TRUE, "idle", "FILE"); 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); } char *get_default_printer(BackendObj *b) { if(b->default_printer == NULL) return "Save_As_PDF"; return b->default_printer; } GVariant *pack_option(const Option *opt) { GVariant **temp = g_new(GVariant *, 4); temp[0] = g_variant_new_string(opt->name); temp[1] = g_variant_new_string(opt->default_value); temp[2] = g_variant_new_int32(opt->num_supported); temp[3] = pack_string_array(opt->num_supported, opt->supported_values); GVariant *option_variant = g_variant_new_tuple(temp, 4); g_free(temp); return option_variant; } void MSG_LOG(const char *msg, int msg_level) { if(MSG_LOG_LEVEL >= msg_level) { printf("%s\n", msg); fflush(stdout); } } void print_file(const char *file_path, const char *final_file_path) { FILE *fin = fopen(file_path, "rb"); FILE *fout = fopen(final_file_path, "w"); size_t bytes; char buffer[65536]; char msg[200]; sprintf(msg, "Printing the file %s to the destination file %s.\n", file_path, final_file_path); MSG_LOG(msg, INFO); while((bytes = fread(buffer, 1, sizeof(buffer), fin)) > 0) { fwrite(buffer, bytes, 1, fout); if(fwrite == 0) { MSG_LOG("Cannot write to the given file.\n", ERR); exit(EXIT_FAILURE); } } } void free_Dialog(Dialog *d) { free(d->printers); free(d); } void free_string(char *str) { if(str) free(str); } cpdb-backend-file-1.0.1/src/backend_helper.h000066400000000000000000000026111333632516300206160ustar00rootroot00000000000000#ifndef _BACKEND_HELPER_H #define _BACKEND_HELPER_H #include #include #include #include #define INFO 3 #define WARN 2 #define ERR 1 #define MSG_LOG_LEVEL INFO typedef struct _BackendObj { GDBusConnection *dbus_connection; PrintBackend *skeleton; char *obj_path; GHashTable *dialogs; int num_frontends; char *default_printer; } BackendObj; typedef struct _Option { char *name; int num_supported; char **supported_values; char *default_value; } Option; typedef struct _FilePrinter { char *name; char *info; char *location; char *state; int num_options; Option *options; gboolean is_accepting_jobs; } FilePrinter; typedef struct _Dialog { int cancel; FilePrinter *printers; gboolean keep_alive; } Dialog; BackendObj *get_new_BackendObj(); Dialog *get_new_Dialog(); FilePrinter *get_FilePrinter(); void add_frontend(BackendObj *b, const char *dialog_name); void remove_frontend(BackendObj *b, const char *dialog_name); void connect_to_dbus(BackendObj *b, char *obj_path); void send_printer_added_signal(BackendObj *b, const char *dialog_name); char *get_default_printer(BackendObj *b); GVariant *pack_option(const Option *opt); void MSG_LOG(const char *msg, int msg_level); void print_file(const char *file_path, const char *final_file_path); void free_Dialog(Dialog *d); void free_string(char *str); #endif cpdb-backend-file-1.0.1/src/print_backend_file.c000066400000000000000000000214231333632516300214670ustar00rootroot00000000000000#include "print_backend_file.h" int main() { //printf("Hello World!\n"); GMainLoop *loop = g_main_loop_new(NULL, FALSE); b = get_new_BackendObj(); acquire_session_bus_name(BUS_NAME); g_main_loop_run(loop); } static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { b->dbus_connection = connection; b->skeleton = print_backend_skeleton_new(); connect_to_signals(); connect_to_dbus(b, OBJECT_PATH); } static void acquire_session_bus_name(char *bus_name) { g_bus_own_name(G_BUS_TYPE_SESSION, bus_name, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, on_name_acquired, NULL, NULL, NULL); } static gboolean on_handle_activate_backend(PrintBackend *interface, GDBusMethodInvocation *invocation, gpointer user_data) { const char *dialog_name = g_dbus_method_invocation_get_sender(invocation); add_frontend(b, dialog_name); send_printer_added_signal(b, dialog_name); 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); Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if(d == NULL) { MSG_LOG("Invalid dialog name.\n", ERR); exit(EXIT_FAILURE); } if(strcmp(d->printers->name, printer_name)) { printf("Printer '%s' does not exist for the dialog %s.\n", printer_name, dialog_name); exit(EXIT_FAILURE); } print_file(file_path, final_file_path); print_backend_complete_print_file(interface, invocation, "File printed"); /* (Currently Disabled) Printing a file must be the last operation. */ //d->cancel = 1; //remove_frontend(b, dialog_name); if(b->num_frontends == 0) { g_message("No frontends connected .. exiting backend.\n"); exit(EXIT_SUCCESS); } return TRUE; } 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 user_data) { g_message("Stop backend signal from %s\n", sender_name); Dialog *d = (Dialog *)(g_hash_table_lookup(b->dialogs, sender_name)); if(d->keep_alive) return; d->cancel = 1; remove_frontend(b, sender_name); if(b->num_frontends == 0) { 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 user_data) { g_message("Refresh backend signal from %s\n", sender_name); } static gboolean on_handle_get_default_printer(PrintBackend *interface, GDBusMethodInvocation *invocation, gpointer user_data) { char *printer = get_default_printer(b); printf("%s\n", printer); print_backend_complete_get_default_printer(interface, invocation, printer); 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); Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if(d == NULL) { MSG_LOG("Invalid dialog name.\n", ERR); exit(EXIT_FAILURE); } if(strcmp(d->printers->name, printer_name)) { printf("Printer '%s' does not exist for the dialog %s.\n", printer_name, dialog_name); exit(EXIT_FAILURE); } const char *state = d->printers->state; printf("%s is %s\n", printer_name, state); print_backend_complete_get_printer_state(interface, invocation, state); return TRUE; } 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); Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if(d == NULL) { MSG_LOG("Invalid dialog name.\n", ERR); exit(EXIT_FAILURE); } if(strcmp(d->printers->name, printer_name)) { printf("Printer '%s' does not exist for the dialog %s.\n", printer_name, dialog_name); exit(EXIT_FAILURE); } print_backend_complete_is_accepting_jobs(interface, invocation, d->printers->is_accepting_jobs); 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); Dialog *d = (Dialog *)g_hash_table_lookup(b->dialogs, dialog_name); if(d == NULL) { MSG_LOG("Invalid dialog name.\n", ERR); exit(EXIT_FAILURE); } if(strcmp(d->printers->name, printer_name)) { printf("Printer '%s' does not exist for the dialog %s.\n", printer_name, dialog_name); exit(EXIT_FAILURE); } int count = d->printers->num_options; Option *options; options = d->printers->options; GVariantBuilder *builder; builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssia(s))")); for(int i=0; iskeleton; g_signal_connect(skeleton, "handle-activate-backend", G_CALLBACK(on_handle_activate_backend), NULL); g_signal_connect(skeleton, "handle-print-file", G_CALLBACK(on_handle_print_file), NULL); g_signal_connect(skeleton, "handle-get-default-printer", G_CALLBACK(on_handle_get_default_printer), NULL); g_signal_connect(skeleton, "handle-get-printer-state", G_CALLBACK(on_handle_get_printer_state), NULL); g_signal_connect(skeleton, "handle-is-accepting-jobs", G_CALLBACK(on_handle_is_accepting_jobs), NULL); g_signal_connect(skeleton, "handle-get-all-options", G_CALLBACK(on_handle_get_all_options), NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, "org.openprinting.PrintFrontend", REFRESH_BACKEND_SIGNAL, NULL, NULL, 0, on_refresh_backend, NULL, NULL); g_dbus_connection_signal_subscribe(b->dbus_connection, NULL, "org.openprinting.PrintFrontend", STOP_BACKEND_SIGNAL, NULL, NULL, 0, on_stop_backend, NULL, NULL); } cpdb-backend-file-1.0.1/src/print_backend_file.h000066400000000000000000000056551333632516300215050ustar00rootroot00000000000000#include #include #include #include #include #include "backend_helper.h" #define BUS_NAME "org.openprinting.Backend.FILE" #define OBJECT_PATH "/" BackendObj *b; static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data); static void acquire_session_bus_name(char *bus_name); static gboolean on_handle_activate_backend(PrintBackend *interface, GDBusMethodInvocation *invocation, gpointer user_data); 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); 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 user_data); 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 user_data); static gboolean on_handle_get_default_printer(PrintBackend *interface, GDBusMethodInvocation *invocation, gpointer user_data); static gboolean on_handle_get_printer_state(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data); static gboolean on_handle_is_accepting_jobs(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data); static gboolean on_handle_get_all_options(PrintBackend *interface, GDBusMethodInvocation *invocation, const gchar *printer_name, gpointer user_data); void connect_to_signals();