From 66cb28cb1f13ed3e9c7a1feb999e41f0d5060ada Mon Sep 17 00:00:00 2001
From: Nicolas Schichan <nschichan@freebox.fr>
Date: Wed, 6 Mar 2024 18:05:36 +0100
Subject: [PATCH] samba3: add and use print_fbx ipc protocol instead of
 print_generic code

---
 src/Makefile.in          |   2 +-
 src/include/printing.h   |   1 +
 src/printing/print_fbx.c | 539 +++++++++++++++++++++++++++++++++++++++
 src/printing/printing.c  |   2 +-
 src/printing/smbipc.h    |  69 +++++
 5 files changed, 611 insertions(+), 2 deletions(-)
 create mode 100644 src/printing/print_fbx.c
 create mode 100644 src/printing/smbipc.h

diff --git src/Makefile.in src/Makefile.in
index 34b4490..39c783c 100644
--- src/Makefile.in
+++ src/Makefile.in
@@ -488,7 +488,7 @@ SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(LIBSMB_OBJ) \
 PRINTING_OBJ = printing/pcap.o printing/print_svid.o printing/print_aix.o \
                printing/print_cups.o printing/print_generic.o \
                printing/lpq_parse.o printing/load.o \
-               printing/print_iprint.o printing/print_test.o
+               printing/print_iprint.o printing/print_test.o printing/print_fbx.o
 
 PRINTBASE_OBJ = printing/notify.o printing/printing_db.o
 PRINTBACKEND_OBJ = printing/printing.o printing/nt_printing.o $(PRINTBASE_OBJ)
diff --git src/include/printing.h src/include/printing.h
index 21caff0..c99710e 100644
--- src/include/printing.h
+++ src/include/printing.h
@@ -64,6 +64,7 @@ struct printif
 };
 
 extern struct printif	generic_printif;
+extern struct printif	fbx_printif;
 
 #ifdef HAVE_CUPS
 extern struct printif	cups_printif;
diff --git src/printing/print_fbx.c src/printing/print_fbx.c
new file mode 100644
index 0000000..241ea1f
--- /dev/null
+++ src/printing/print_fbx.c
@@ -0,0 +1,539 @@
+/*
+ * print_fbx.c for samba-3.0.37
+ * Created by <nschichan@freebox.fr> on Tue Mar  5 17:38:27 2024
+ */
+
+#include "includes.h"
+#include "printing.h"
+
+#include "smbipc.h"
+
+#define FBXLPD_SMBIPC_SOCK_NAME	"\0fbxlpd_smbipc"
+
+#define pr_debug(...)	DEBUG(10, ("SMBIPC: " __VA_ARGS__))
+#define pr_info(...)	DEBUG(3, ("SMBIPC: " __VA_ARGS__))
+#define pr_err(...)	DEBUG(0, ("SMBIPC: " __VA_ARGS__))
+
+static void const_free(const void *ptr)
+{
+	return free((void*)ptr);
+}
+
+#define __maybe_unused __attribute__((unused))
+
+/*
+ * for the SMBIPC protocol:
+ * - the remote sends an u32 command.
+ *
+ * - the remote sends one or more tlv structures & values with the
+ *   appropriate tag, dependent on the command
+ *
+ * - the local side replies with a TLV tagged with a TAG_REPLY with
+ *   the result of the command.
+ */
+
+/*
+ * put a tlv structure followed by a string on the wire.
+ */
+static int smbipc_put_string(int fd, u32 tag, const char *str)
+{
+	struct tlv tlv;
+	ssize_t wlen;
+
+	if (strlen(str) > IPC_MAX_STRING_LEN)
+		return -1;
+
+	memset(&tlv, 0, sizeof (tlv));
+	tlv.tag = tag;
+	tlv.type = TYPE_STRING;
+	tlv.size = strlen(str);
+
+	wlen = write(fd, &tlv, sizeof (tlv));
+	if ((size_t)wlen != sizeof (tlv))
+		return -1;
+	wlen = write(fd, str, strlen(str));
+
+	if ((size_t)wlen != strlen(str))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * get a string typed TLV structure followed by a string from the
+ * wire.
+ */
+static int smbipc_get_string(int fd, u32 tag, const char **out_str)
+{
+	struct tlv tlv;
+	char *ret;
+
+	if (read(fd, &tlv, sizeof (tlv)) != sizeof (tlv))
+		return -1;
+
+	if (tlv.type != TYPE_STRING)
+		return -1;
+
+	if (tlv.tag != tag)
+		return -1;
+
+	if (tlv.size > IPC_MAX_STRING_LEN)
+		return -1;
+
+	ret = calloc(1, tlv.size + 1);
+	if (read(fd, ret, tlv.size) != tlv.size) {
+		free(ret);
+		return -1;
+	}
+	*out_str = ret;
+	return 0;
+
+}
+
+/*
+ * put an u32 preceded by a tlv structure on the wire.
+ */
+static int smbipc_put_u32(int fd, u32 tag, u32 v)
+{
+	struct tlv tlv;
+
+	memset(&tlv, 0, sizeof (tlv));
+	tlv.tag = tag;
+	tlv.type = TYPE_U32;
+	tlv.size = sizeof (u32);
+
+	if (write(fd, &tlv, sizeof (tlv)) != sizeof (tlv))
+		return -1;
+	if (write(fd, &v, sizeof (v)) != sizeof (v))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * get an u32 value from the wire
+ */
+static int smbipc_get_u32(int fd, u32 tag, u32 *v)
+{
+	struct tlv tlv;
+
+	if (read(fd, &tlv, sizeof (tlv)) != sizeof (tlv))
+		return -1;
+
+	if (tlv.type != TYPE_U32)
+		return -1;
+
+	if (tlv.tag != tag)
+		return -1;
+
+	if (tlv.size != sizeof (u32))
+		return -1;
+
+	if (read (fd, v, sizeof (*v)) != sizeof (*v))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * put an u64 preceded by a tlv structure on the wire.
+ */
+static int __maybe_unused smbipc_put_u64(int fd, u32 tag, u64 v)
+{
+	struct tlv tlv;
+
+	memset(&tlv, 0, sizeof (tlv));
+	tlv.tag = tag;
+	tlv.type = TYPE_U64;
+	tlv.size = sizeof (u64);
+
+	if (write(fd, &tlv, sizeof (tlv)) != sizeof (tlv))
+		return -1;
+	if (write(fd, &v, sizeof (v)) != sizeof (v))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * get an u32 value from the wire
+ */
+static int smbipc_get_u64(int fd, u32 tag, u64 *v)
+{
+	struct tlv tlv;
+
+	if (read(fd, &tlv, sizeof (tlv)) != sizeof (tlv))
+		return -1;
+
+	if (tlv.type != TYPE_U64)
+		return -1;
+
+	if (tlv.tag != tag)
+		return -1;
+
+	if (tlv.size != sizeof (u64))
+		return -1;
+
+	if (read (fd, v, sizeof (*v)) != sizeof (*v))
+		return -1;
+
+	return 0;
+}
+
+/*
+ *
+ */
+static int smbipc_connect(void)
+{
+	int ret;
+	struct sockaddr_un sun;
+	socklen_t salen;
+
+	ret = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (ret < 0) {
+		pr_err("socket: %m\n");
+		return -1;
+	}
+
+	memset(&sun, 0, sizeof (sun));
+	sun.sun_family = AF_UNIX;
+	salen = sizeof (sa_family_t) + sizeof(FBXLPD_SMBIPC_SOCK_NAME) - 1;
+	memcpy(sun.sun_path, FBXLPD_SMBIPC_SOCK_NAME,
+	       sizeof (FBXLPD_SMBIPC_SOCK_NAME) - 1);
+
+	if (connect(ret, (struct sockaddr *)&sun, salen) < 0) {
+		pr_err("connect: %m\n");
+		goto err_close_smbipc_fd;
+	}
+
+	return ret;
+
+err_close_smbipc_fd:
+	close(ret);
+	return -1;
+}
+
+/*
+ *
+ */
+static int write_cmd(int ipc_fd, u32 cmd)
+{
+	return write(ipc_fd, &cmd, sizeof (cmd)) != sizeof (cmd) ? -1 : 0;
+}
+
+static int get_queue_entry(int ipc_fd, print_queue_struct *qe)
+{
+	u32 start, end;
+	u32 job_id, job_state;
+	u64 job_byte_size, job_submit_date;
+	const char *job_desc = NULL;
+	int ret = -1;
+
+	if (smbipc_get_u32(ipc_fd, TAG_JOB_START, &start) < 0)
+		goto done;
+	if (start != JOB_START_MAGIC)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_JOB_ID, &job_id) < 0)
+		goto done;
+
+	if (smbipc_get_string(ipc_fd, TAG_JOB_DESC, &job_desc) < 0)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_JOB_STATE, &job_state) < 0)
+		goto done;
+
+	if (smbipc_get_u64(ipc_fd, TAG_JOB_BYTE_SIZE,
+			   &job_byte_size) < 0)
+		goto done;
+
+	if (smbipc_get_u64(ipc_fd, TAG_JOB_SUBMIT_DATE,
+			   &job_submit_date) < 0)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_JOB_END, &end) < 0)
+		goto done;
+	if (end != JOB_END_MAGIC)
+		goto done;
+
+	qe->job = job_id;
+	qe->size = job_byte_size;
+
+	switch (job_state) {
+	case JOB_STATE_QUEUED:
+		qe->status = LPQ_QUEUED;
+		break;
+	case JOB_STATE_PAUSED:
+		qe->status = LPQ_PAUSED;
+		break;
+	case JOB_STATE_PRINTING:
+	default:
+		qe->status = LPQ_PRINTING;
+	}
+
+	qe->priority = 0;
+	qe->time = job_submit_date;
+	fstrcpy(qe->fs_user, "freebox");
+	fstrcpy(qe->fs_file, job_desc);
+
+	pr_debug("job id: %u\n", job_id);
+	pr_debug("job desc %s\n", job_desc);
+	pr_debug("job size: %u\n", (u32)job_byte_size);
+
+
+	ret = 0;
+done:
+	const_free(job_desc);
+	return ret;
+}
+
+static int fbx_queue_get(const char *printer_name,
+			 enum printing_types printing_type,
+			 char *lpq_command,
+			 print_queue_struct **q,
+			 print_status_struct *status)
+{
+	int ipc_fd;
+	u32 printer_state;
+	u32 job, job_count;
+	int ret = -1;
+	print_queue_struct *queue = NULL;
+	u32 reply;
+
+	memset(status, 0, sizeof (*status));
+
+
+	pr_debug("print_fbx: %s\n", __func__);
+
+	ipc_fd = smbipc_connect();
+	if (ipc_fd < 0) {
+		pr_err("unable to connect to fbxlpd.\n");
+		return -1;
+	}
+	pr_debug("connected to fbxlpd.\n");
+
+	if (write_cmd(ipc_fd, SMBIPC_CMD_GET_STATUS) < 0)
+		goto done;
+
+	if (smbipc_put_string(ipc_fd, TAG_PRINTER_NAME, printer_name) < 0)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_PRINTER_STATE, &printer_state) < 0)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_QUEUED_JOBS_COUNT, &job_count) < 0)
+		goto done;
+
+	pr_debug("printer queue state: %s\n",
+	       printer_state == PRINTER_STATE_QUEUE_ACTIVE ? "active" : "paused");
+	pr_debug("queued job count: %u\n", job_count);
+
+	switch (printer_state) {
+	case PRINTER_STATE_QUEUE_ACTIVE:
+		status->status = LPSTAT_OK;
+		fstrcpy(status->message, "online");
+		break;
+	case PRINTER_STATE_QUEUE_PAUSED:
+	default:
+		status->status = LPSTAT_STOPPED;
+		fstrcpy(status->message, "offline");
+		break;
+	}
+
+	queue = SMB_MALLOC_ARRAY(print_queue_struct, job_count);
+	memset(queue, 0, sizeof (*queue) * job_count);
+	for (job = 0; job < job_count; ++job) {
+		get_queue_entry(ipc_fd, &queue[job]);
+	}
+
+	if (smbipc_get_u32(ipc_fd, TAG_REPLY, &reply) < 0) {
+		pr_err("unable to read reply.\n");
+		goto done;
+	}
+
+
+	if (!reply)
+		ret = 0;
+
+done:
+	if (ret) {
+		pr_err("fbx_queue_get: failed\n");
+		SAFE_FREE(queue);
+	} else
+		*q = queue;
+
+	close(ipc_fd);
+	return ret ? ret : (int)job_count;
+}
+
+static int __start_stop_queue(int snum, bool do_pause)
+{
+	int ipc_fd;
+	int ret = -1;
+	u32 reply;
+	const char *printer = PRINTERNAME(snum);
+
+	if (!printer || !*printer)
+		return -1;
+
+	ipc_fd = smbipc_connect();
+	if (ipc_fd < 0) {
+		pr_err("unable to connect to fbxlpd.\n");
+		return -1;
+	}
+	pr_debug("connected to fbxlpd.\n");
+
+	if (write_cmd(ipc_fd, SMBIPC_CMD_START_STOP_QUEUE) < 0)
+		goto done;
+
+	if (smbipc_put_string(ipc_fd, TAG_PRINTER_NAME, printer) < 0)
+		goto done;
+	if (smbipc_put_u32(ipc_fd, TAG_START_STOP_PARAM, do_pause) < 0)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_REPLY, &reply) < 0)
+		goto done;
+
+	if (!reply)
+		ret = 0;
+done:
+	close(ipc_fd);
+	return ret;
+}
+
+static int fbx_queue_pause(int snum)
+{
+	pr_debug("print_fbx: %s\n", __func__);
+	return __start_stop_queue(snum, true);
+}
+
+static int fbx_queue_resume(int snum)
+{
+	pr_debug("print_fbx: %s\n", __func__);
+	return __start_stop_queue(snum, false);
+}
+
+static int __generic_job_command(u32 cmd, const char *printer, u32 job_id)
+{
+	int ipc_fd;
+	int ret = -1;
+	u32 reply;
+
+	ipc_fd = smbipc_connect();
+	if (ipc_fd < 0) {
+		pr_err("unable to connect to fbxlpd.\n");
+		return -1;
+	}
+	pr_debug("connected to fbxlpd.\n");
+
+	if (write_cmd(ipc_fd, cmd) < 0)
+		goto done;
+
+	if (smbipc_put_string(ipc_fd, TAG_PRINTER_NAME, printer) < 0)
+		goto done;
+
+	if (smbipc_put_u32(ipc_fd, TAG_JOB_ID, job_id) < 0)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_REPLY, &reply) < 0) {
+		pr_err("unable to read reply.\n");
+		goto done;
+	}
+
+	if (!reply)
+		ret = 0;
+done:
+	close(ipc_fd);
+	return ret;
+}
+
+static int fbx_job_delete(const char *sharename, const char *lprm_command,
+			  struct printjob *j)
+{
+	pr_debug("print_fbx: %s\n", __func__);
+	return __generic_job_command(SMBIPC_CMD_REMOVE_JOB, sharename, j->sysjob);
+}
+
+static int __job_pause(int snum, struct printjob *j, bool do_pause)
+{
+	const char *printer = PRINTERNAME(snum);
+
+	if (!printer || !*printer)
+		return -1;
+
+	return __generic_job_command(do_pause ?
+				     SMBIPC_CMD_PAUSE_JOB : SMBIPC_CMD_RESUME_JOB,
+				     printer, j->sysjob);
+}
+
+static int fbx_job_pause(int snum, struct printjob *j)
+{
+	pr_debug("print_fbx: %s\n", __func__);
+	return __job_pause(snum, j, true);
+}
+
+static int fbx_job_resume(int snum, struct printjob *j)
+{
+	pr_debug("print_fbx: %s\n", __func__);
+	return __job_pause(snum, j, false);
+}
+
+static int fbx_job_submit(int snum, struct printjob *j)
+{
+	int ipc_fd;
+	int ret = -1;
+	u32 reply;
+	const char *printer = PRINTERNAME(snum);
+	const char *filename;
+
+	pr_debug("print_fbx: %s\n", __func__);
+
+	if (!printer || !*printer)
+		return -1;
+
+	ipc_fd = smbipc_connect();
+	if (ipc_fd < 0) {
+		pr_debug("unable to connect to fbxlpd.\n");
+		return -1;
+	}
+	pr_debug("connected to fbxlpd.\n");
+
+	if (write_cmd(ipc_fd, SMBIPC_CMD_ADD_JOB) < 0)
+		goto done;
+
+	filename = strrchr(j->filename, '/');
+	if (!filename)
+		filename = j->filename;
+	else
+		filename++;
+
+
+	if (smbipc_put_string(ipc_fd, TAG_PRINTER_NAME, printer) < 0)
+		goto done;
+	if (smbipc_put_string(ipc_fd, TAG_JOB_FILE, filename) < 0)
+		goto done;
+	if (smbipc_put_string(ipc_fd, TAG_JOB_DESC, j->jobname) < 0)
+		goto done;
+
+	if (smbipc_get_u32(ipc_fd, TAG_REPLY, &reply) < 0) {
+		pr_err("job submit fail job %s (%s)\n", j->jobname, filename);
+		goto done;
+	}
+
+	if (!reply)
+		ret = 0;
+done:
+	close(ipc_fd);
+	return ret;
+}
+
+struct printif fbx_printif = {
+	.type		= DEFAULT_PRINTING,
+	.queue_get	= fbx_queue_get,
+	.queue_pause	= fbx_queue_pause,
+	.queue_resume	= fbx_queue_resume,
+	.job_delete	= fbx_job_delete,
+	.job_pause	= fbx_job_pause,
+	.job_resume	= fbx_job_resume,
+	.job_submit	= fbx_job_submit,
+};
diff --git src/printing/printing.c src/printing/printing.c
index c5b8fd8..266f3e9 100644
--- src/printing/printing.c
+++ src/printing/printing.c
@@ -238,7 +238,7 @@ void printing_end(void)
 
 static struct printif *get_printer_fns_from_type( enum printing_types type )
 {
-	struct printif *printer_fns = &generic_printif;
+	struct printif *printer_fns = &fbx_printif;
 
 #ifdef HAVE_CUPS
 	if ( type == PRINT_CUPS ) {
diff --git src/printing/smbipc.h src/printing/smbipc.h
new file mode 100644
index 0000000..e098b88
--- /dev/null
+++ src/printing/smbipc.h
@@ -0,0 +1,69 @@
+/*
+ * smbipc.h for fbxlpd
+ * Created by <nschichan@freebox.fr> on Mon Mar  4 14:06:53 2024
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint8_t u8;
+
+enum {
+	SMBIPC_CMD_QUERY = 0x420,
+	SMBIPC_CMD_ADD_JOB,
+	SMBIPC_CMD_START_STOP_QUEUE,
+	SMBIPC_CMD_GET_STATUS,
+	SMBIPC_CMD_REMOVE_JOB,
+	SMBIPC_CMD_PAUSE_JOB,
+	SMBIPC_CMD_RESUME_JOB,
+};
+
+enum {
+	TYPE_STRING = 0xaa5500,
+	TYPE_U32,
+	TYPE_U64,
+};
+
+enum {
+	TAG_PRINTER_NAME = 0x21214200,
+	TAG_JOB_DESC,
+	TAG_JOB_FILE,
+	TAG_REPLY,
+	TAG_START_STOP_PARAM,
+	TAG_PRINTER_STATE,
+	TAG_QUEUED_JOBS_COUNT,
+	TAG_JOB_START,
+	TAG_JOB_END,
+	TAG_JOB_ID,
+	TAG_JOB_STATE,
+	TAG_JOB_BYTE_SIZE,
+	TAG_JOB_SUBMIT_DATE,
+};
+
+enum {
+	JOB_START_MAGIC = 0x32dddb3e,
+	JOB_END_MAGIC = 0xc549b344,
+};
+
+enum {
+	PRINTER_STATE_QUEUE_ACTIVE,
+	PRINTER_STATE_QUEUE_PAUSED,
+};
+
+enum {
+	JOB_STATE_QUEUED,
+	JOB_STATE_PRINTING,
+	JOB_STATE_PAUSED,
+	JOB_STATE_UNKNOWN,
+};
+
+struct tlv {
+	u32 tag;
+	u32 type;
+	u32 size;
+};
+
+#define IPC_MAX_STRING_LEN	128
-- 
2.34.1

