summaryrefslogtreecommitdiffstats
path: root/lib/printer_driver/printer_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/printer_driver/printer_driver.c')
-rw-r--r--lib/printer_driver/printer_driver.c1252
1 files changed, 1252 insertions, 0 deletions
diff --git a/lib/printer_driver/printer_driver.c b/lib/printer_driver/printer_driver.c
new file mode 100644
index 0000000..35260c2
--- /dev/null
+++ b/lib/printer_driver/printer_driver.c
@@ -0,0 +1,1252 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Guenther Deschner 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "rpc_client/init_spoolss.h"
+#include "libgpo/gpo_ini.h"
+#include "printer_driver.h"
+
+#define ADD_TO_ARRAY(mem_ctx, type, elem, array, num) \
+do { \
+ *(array) = talloc_realloc(mem_ctx, (*(array)), type, (*(num))+1); \
+ SMB_ASSERT((*(array)) != NULL); \
+ (*(array))[*(num)] = (elem); \
+ (*(num)) += 1; \
+} while (0)
+
+
+/* GetPrinterDriverDirectory -> drivers and dependent files */
+#define PRINTER_INF_DIRID_66000
+
+/* GetPrintProcessorDirectory -> print processors */
+#define PRINTER_INF_DIRID_66001
+
+/* GetColorDirectory -> color profiles */
+#define PRINTER_INF_DIRID_66003
+
+static const char *get_string_unquote(const char *s)
+{
+ bool ok;
+ size_t len;
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ len = strlen(s);
+ if (len < 2) {
+ return s;
+ }
+
+ if (s[0] == '"' && s[len-1] == '"') {
+ ok = trim_string(discard_const(s), "\"", "\"");
+ if (!ok) {
+ return NULL;
+ }
+ }
+
+ return s;
+}
+
+/*
+ * '%STRING%' indicates STRING is localized in the [Strings] section
+ */
+
+static const char *get_string_token(struct gp_inifile_context *ctx,
+ const char *s)
+{
+ NTSTATUS status;
+ bool ok;
+ char *key;
+ const char *s2;
+
+ if (s != NULL && s[0] != '%' && s[strlen(s)-1] != '%') {
+ return s;
+ }
+
+ ok = trim_string(discard_const(s), "%", "%");
+ if (!ok) {
+ return NULL;
+ }
+
+ key = talloc_asprintf(ctx, "Strings:%s", s);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ status = gp_inifile_getstring(ctx, key, &s2);
+ talloc_free(key);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* what can you do... */
+ return s;
+ }
+
+ return s2;
+}
+
+static NTSTATUS gp_inifile_getstring_ext(struct gp_inifile_context *ctx,
+ const char *key,
+ const char **ret)
+{
+ NTSTATUS status;
+ const char *s;
+
+ status = gp_inifile_getstring(ctx, key, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ s = get_string_unquote(s);
+ if (s == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (s[0] == '%' && s[strlen(s)-1] == '%') {
+ s = get_string_token(ctx, s);
+ }
+
+ s = get_string_unquote(s);
+ if (s == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *ret = s;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS find_manufacturer_name(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *section_name,
+ const char **manufacturer_name)
+{
+ NTSTATUS status;
+ size_t num_keys = 0;
+ const char **keys = NULL;
+ const char **values = NULL;
+ const char *s;
+ char *p;
+
+ status = gp_inifile_enum_section(ctx, section_name, &num_keys, &keys, &values);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (num_keys < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ s = talloc_strdup(mem_ctx, keys[0]);
+ if (s == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strchr(s, ':');
+ if (p == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *p = '\0';
+ p++;
+
+ s = get_string_unquote(p);
+ if (s == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ s = get_string_token(ctx, s);
+
+ s = get_string_unquote(s);
+ if (s == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (s != NULL) {
+ *manufacturer_name = talloc_strdup(mem_ctx, s);
+ if (*manufacturer_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(keys);
+ talloc_free(values);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS find_manufacturer_url(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *section_name,
+ const char *manufacturer_name,
+ const char **manufacturer_url)
+{
+ NTSTATUS status;
+ size_t num_keys = 0;
+ const char **keys = NULL;
+ const char **values = NULL;
+ const char *s;
+ char *p;
+
+ status = gp_inifile_enum_section(ctx, section_name, &num_keys, &keys, &values);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (num_keys < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ p = strchr(keys[0], ':');
+ if (p == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *p = '\0';
+ p++;
+
+ s = get_string_unquote(p);
+ if (s == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ s = get_string_token(ctx, s);
+
+ s = get_string_unquote(s);
+ if (s == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (strequal(s, manufacturer_name)) {
+ s = get_string_unquote(values[0]);
+ if (s == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ if (s != NULL) {
+ *manufacturer_url = talloc_strdup(mem_ctx, s);
+ if (*manufacturer_url == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(keys);
+ talloc_free(values);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS add_string_to_spoolss_array(TALLOC_CTX *mem_ctx,
+ const char *s,
+ struct spoolss_StringArray **r)
+{
+ size_t count = 2;
+ struct spoolss_StringArray *a = *r;
+ bool ok;
+ int i;
+
+ if (a == NULL) {
+ a = talloc_zero(mem_ctx, struct spoolss_StringArray);
+ if (a == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (a->string == NULL) {
+ a->string = talloc_zero_array(a, const char *, count);
+ if (a->string == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ for (i = 0; a->string[i] != NULL; i++) { ;; }
+ count = i;
+
+ ok = add_string_to_array(mem_ctx, s, &a->string, &count);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ a->string = talloc_realloc(mem_ctx, a->string, const char *, count + 1);
+ if (a->string == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a->string[count] = NULL;
+
+ *r = a;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS add_dependent_driver_file(TALLOC_CTX *mem_ctx,
+ const char *file,
+ struct spoolss_StringArray **r)
+{
+ char *p;
+
+ if (file == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (file[0] == '@') {
+ file++;
+ }
+
+ p = strchr(file, ',');
+ if (p != NULL) {
+ *p = '\0';
+ }
+
+ return add_string_to_spoolss_array(mem_ctx, file, r);
+}
+
+/*
+ * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-manufacturer-section
+ *
+ * [Manufacturer]
+ * "Kyocera"=Kyocera,NTx86.5.1,NTx86.6.0,NTamd64.5.1,NTamd64.6.0
+ */
+
+static NTSTATUS enum_devices_in_toc(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ size_t *pnum_devices,
+ const char ***pdevices,
+ const char ***pdevice_values)
+{
+ NTSTATUS status;
+ size_t i, num_manufacturers = 0;
+ const char **manufacturers = NULL;
+ const char **values = NULL;
+ char *p;
+ bool ok;
+
+ status = gp_inifile_enum_section(ctx, "Manufacturer", &num_manufacturers, &manufacturers, &values);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (i = 0; i < num_manufacturers; i++) {
+
+ const char *models_section_name;
+ const char *s;
+ char **decorations;
+ int j;
+
+ DEBUG(11,("processing manufacturer: %s\n", manufacturers[i]));
+
+ status = gp_inifile_getstring(ctx, manufacturers[i], &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ decorations = str_list_make_v3(mem_ctx, s, ",");
+ if (decorations == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ models_section_name = decorations[0];
+
+ for (j = 1; decorations[j] != NULL; j++) {
+
+ /*
+ * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-models-section
+ */
+
+ const char *decorated_models_section_name;
+ size_t d, num_devices = 0;
+ const char **devices = NULL;
+ const char **device_values = NULL;
+ size_t c = 0;
+
+ decorated_models_section_name = talloc_asprintf(mem_ctx, "%s.%s",
+ models_section_name,
+ decorations[j]);
+ if (decorated_models_section_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(11,("processing decorated models_section_name: %s\n",
+ decorated_models_section_name));
+
+ status = gp_inifile_enum_section(ctx, decorated_models_section_name,
+ &num_devices, &devices,
+ &device_values);
+ for (d = 0; d < num_devices; d++) {
+
+ DEBUG(11,("processing device: %s\n",
+ devices[d]));
+
+ s = talloc_strdup(mem_ctx, devices[d]);
+ if (s == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strchr(s, ':');
+ if (p == NULL) {
+ return NT_STATUS_DRIVER_INTERNAL_ERROR;
+ }
+
+ *p = '\0';
+ p++;
+
+ s = get_string_unquote(p);
+
+ ok = add_string_to_array(mem_ctx, s, pdevices, pnum_devices);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ok = add_string_to_array(mem_ctx, device_values[d], pdevice_values, &c);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS find_device_in_toc(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *device_description,
+ const char **value)
+{
+ NTSTATUS status;
+ size_t d, num_devices = 0;
+ const char **devices = NULL;
+ const char **device_values = NULL;
+
+ if (device_description == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = enum_devices_in_toc(ctx, mem_ctx,
+ &num_devices,
+ &devices,
+ &device_values);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (d = 0; d < num_devices; d++) {
+
+ if (strequal(device_description, devices[d])) {
+
+ DEBUG(10,("found device_description: %s\n",
+ device_description));
+
+ *value = talloc_strdup(mem_ctx, device_values[d]);
+ if (*value == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ DEBUGADD(10,("and returned: %s\n", *value));
+
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_DRIVER_INTERNAL_ERROR;
+}
+
+/*
+ * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-copyfiles-directive
+ */
+
+static NTSTATUS process_driver_section_copyfiles(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *driver_section,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ size_t i, num_keys = 0;
+ char *p, *key;
+ const char **keys = NULL;
+ const char **values = NULL;
+ char *str;
+ const char *s;
+
+ key = talloc_asprintf(mem_ctx, "%s:%s", driver_section, "CopyFiles");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("Checking for CopyFiles entry in %s\n", driver_section));
+
+ status = gp_inifile_getstring(ctx, key, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(10,("these are the files to copy: %s\n", s));
+
+ while (next_token_talloc(mem_ctx, &s, &str, ",")) {
+
+ DEBUG(10,("trying section: %s\n", str));
+
+ if (str[0] == '@') {
+ DEBUG(10,("adding dependent driver file: %s\n", str));
+ status = add_dependent_driver_file(mem_ctx, str, &r->dependent_files);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ continue;
+ }
+
+ status = gp_inifile_enum_section(ctx, str, &num_keys, &keys, &values);
+ if (NT_STATUS_IS_OK(status)) {
+ for (i = 0; i < num_keys; i++) {
+ p = strchr(keys[i], ':');
+ if (p == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *p = '\0';
+ p++;
+
+ DEBUG(10,("adding dependent driver file: %s\n", p));
+
+ status = add_dependent_driver_file(mem_ctx, p, &r->dependent_files);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ TALLOC_FREE(keys);
+ TALLOC_FREE(values);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+#define process_driver_section_val(_ctx, _mem_ctx, _section, _r, _key, _element) \
+do { \
+ NTSTATUS _status; \
+ const char *__key, *_s; \
+ __key = talloc_asprintf(_mem_ctx, "%s:%s", _section, _key); \
+ NT_STATUS_HAVE_NO_MEMORY(__key); \
+ _status = gp_inifile_getstring(_ctx, __key, &_s); \
+ if (NT_STATUS_IS_OK(_status)) { \
+ (_r)->_element = talloc_strdup(mem_ctx, _s); \
+ NT_STATUS_HAVE_NO_MEMORY((_r)->_element); \
+ } \
+} while(0);
+
+static NTSTATUS process_driver_section_colorprofiles(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *section,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ const char *key, *s;
+
+ key = talloc_asprintf(mem_ctx, "%s:%s", section, "ColorProfiles");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_getstring_ext(ctx, key, &s);
+ if (NT_STATUS_IS_OK(status)) {
+
+ status = add_string_to_spoolss_array(mem_ctx, s, &r->color_profiles);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS process_driver_section_printprocessor(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *section,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ char *key, *p;
+ const char *s;
+
+ key = talloc_asprintf(mem_ctx, "%s:%s", section, "PrintProcessor");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_getstring_ext(ctx, key, &s);
+ if (NT_STATUS_IS_OK(status)) {
+ s = get_string_unquote(s);
+
+ p = strchr(s, ',');
+ if (p == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *p = '\0';
+ r->print_processor = talloc_strdup(mem_ctx, s);
+ if (r->print_processor == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS process_driver_section_data_section(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *section,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ char *key;
+ const char *s;
+
+ key = talloc_asprintf(mem_ctx, "%s:%s", section, "DataSection");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_getstring(ctx, key, &s);
+ if (NT_STATUS_IS_OK(status)) {
+ process_driver_section_val(ctx, mem_ctx, s, r,
+ "DriverFile", driver_path);
+ process_driver_section_val(ctx, mem_ctx, s, r,
+ "HelpFile", help_file);
+ process_driver_section_val(ctx, mem_ctx, s, r,
+ "DataFile", data_file);
+ process_driver_section_val(ctx, mem_ctx, s, r,
+ "ConfigFile", config_file);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS process_one_core_driver_section(struct gp_inifile_context *core_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *driver_section,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ size_t i, num_keys = 0;
+ const char **keys = NULL;
+ const char **values = NULL;
+
+ DEBUG(10,("CoreDriverSection is: %s\n", driver_section));
+
+ status = gp_inifile_enum_section(core_ctx, driver_section, &num_keys, &keys, &values);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (i = 0; i < num_keys; i++) {
+
+ status = process_driver_section_copyfiles(core_ctx, mem_ctx, driver_section, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ process_driver_section_val(core_ctx, mem_ctx, driver_section, r,
+ "DriverFile", driver_path);
+ process_driver_section_val(core_ctx, mem_ctx, driver_section, r,
+ "HelpFile", help_file);
+ process_driver_section_val(core_ctx, mem_ctx, driver_section, r,
+ "ConfigFile", config_file);
+
+ status = process_driver_section_colorprofiles(core_ctx, mem_ctx, driver_section, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ talloc_free(keys);
+ talloc_free(values);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * CoreDriverSections="{D20EA372-DD35-4950-9ED8-A6335AFE79F0},UNIDRV_BIDI.OEM,UNIDRV_BIDI_DATA","{D20EA372-DD35-4950-9ED8-A6335AFE79F2},PCLXL.OEM","{D20EA372-DD35-4950-9ED8-A6335AFE79F3},sRGBPROFILE.OEM"
+ */
+static NTSTATUS process_core_driver_sections(struct gp_inifile_context *core_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *value,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ char *p;
+ char **list;
+ int i;
+
+ list = str_list_make_v3(mem_ctx, value, ",");
+ if (list == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; list[i] != NULL; i++) {
+ char **array;
+ int a;
+
+ /* FIXME: do we have to validate the core driver guid ? */
+
+ p = strchr(list[i], ',');
+ if (p != NULL) {
+ *p = '\0';
+ p++;
+ }
+
+ DEBUG(10,("CoreDriverSections we have to process: %s\n", p));
+
+ array = str_list_make_v3(mem_ctx, p, ",");
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (a = 0; array[a] != NULL; a++) {
+
+ if (core_ctx == NULL) {
+ DEBUG(0,("Need to process CoreDriverSections but "
+ "have no Core Driver Context!\n"));
+ return NT_STATUS_DRIVER_INTERNAL_ERROR;
+ }
+
+ status = process_one_core_driver_section(core_ctx, mem_ctx, array[a], r);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-ddinstall-section
+ */
+static NTSTATUS find_driver_files(struct gp_inifile_context *ctx,
+ struct gp_inifile_context *core_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *driver_name,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ char *key;
+ const char *s;
+ const char *value;
+ char *install_section_name;
+ bool ok;
+ char *hw_id;
+
+ status = find_device_in_toc(ctx, mem_ctx, driver_name, &value);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->driver_name = talloc_strdup(mem_ctx, driver_name);
+ if (r->driver_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = next_token_talloc(mem_ctx, &value, &install_section_name, ",");
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10,("driver_name: %s, value: %s, install_section_name: %s\n",
+ driver_name, value, install_section_name));
+
+ /* Hardware Id is optional */
+ ok = next_token_talloc(mem_ctx, &value, &hw_id, ",");
+ if (ok) {
+ r->hardware_id = hw_id;
+ }
+
+ status = process_driver_section_copyfiles(ctx, mem_ctx, install_section_name, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ process_driver_section_val(ctx, mem_ctx, install_section_name, r,
+ "DriverFile", driver_path);
+ process_driver_section_val(ctx, mem_ctx, install_section_name, r,
+ "HelpFile", help_file);
+ process_driver_section_val(ctx, mem_ctx, install_section_name, r,
+ "DataFile", data_file);
+ process_driver_section_val(ctx, mem_ctx, install_section_name, r,
+ "ConfigFile", config_file);
+
+ status = process_driver_section_printprocessor(ctx, mem_ctx, install_section_name, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = process_driver_section_data_section(ctx, mem_ctx, install_section_name, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ key = talloc_asprintf(mem_ctx, "%s:%s", install_section_name, "CoreDriverSections");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_getstring(ctx, key, &s);
+ if (NT_STATUS_IS_OK(status)) {
+
+ DEBUG(10,("found CoreDriverSections: %s\n", s));
+
+ status = process_core_driver_sections(core_ctx, mem_ctx, s, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct inf_context {
+ struct gp_inifile_context *ctx;
+ struct gp_inifile_context *core_ctx;
+};
+
+static NTSTATUS init_inf_context(TALLOC_CTX *mem_ctx,
+ const char *inf_filename,
+ const char *core_filename,
+ struct inf_context **_inf_ctx)
+{
+ NTSTATUS status;
+ struct gp_inifile_context *ctx;
+ struct gp_inifile_context *core_ctx = NULL;
+ struct inf_context *inf_ctx;
+
+ inf_ctx = talloc_zero(mem_ctx, struct inf_context);
+ if (inf_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_init_context_direct(mem_ctx,
+ inf_filename,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("init_inf_context: failed to load %s\n", inf_filename));
+ return status;
+ }
+
+ if (ctx->generated_filename != NULL) {
+ unlink(ctx->generated_filename);
+ }
+
+ if (core_filename != NULL) {
+ status = gp_inifile_init_context_direct(mem_ctx,
+ core_filename,
+ &core_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("init_inf_context: failed to load %s\n", core_filename));
+ return status;
+ }
+
+ if (core_ctx->generated_filename != NULL) {
+ unlink(core_ctx->generated_filename);
+ }
+ }
+
+ inf_ctx->ctx = ctx;
+ inf_ctx->core_ctx = core_ctx;
+
+ *_inf_ctx = inf_ctx;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS process_driver_driverver(struct gp_inifile_context *ctx,
+ struct spoolss_AddDriverInfo8 *r)
+{
+ NTSTATUS status;
+ const char *s;
+ char *p;
+ bool ok;
+ const char *str;
+
+ status = gp_inifile_getstring(ctx, "Version:DriverVer", &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ str = talloc_strdup(ctx, s);
+ if (str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strchr(str, ',');
+ if (p) {
+ *p = '\0';
+ p++;
+ }
+
+ ok = spoolss_timestr_to_NTTIME(str, &r->driver_date);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ok = spoolss_driver_version_to_qword(p, &r->driver_version);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Parse a SourceDisksNames section,
+ * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-sourcedisksnames-section?f=255&MSPPError=-2147217396
+ */
+static NTSTATUS process_source_disk_name(struct gp_inifile_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *short_environment,
+ const char **source_disk_name)
+{
+ NTSTATUS status;
+ bool ok;
+ const char *key;
+ size_t i, num_keys = 0;
+ const char **keys = NULL;
+ const char **values = NULL;
+
+ key = talloc_asprintf(mem_ctx, "SourceDisksNames.%s", short_environment);
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_enum_section(ctx, key, &num_keys, &keys, &values);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (keys == NULL && values == NULL) {
+ key = "SourceDisksNames";
+
+ status = gp_inifile_enum_section(ctx, key, &num_keys, &keys, &values);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ for (i = 0; i < num_keys; i++) {
+
+ /*
+ * 1 = %Disk1%,,,"Amd64"
+ * diskid = disk-description[,[tag-or-cab-file],[unused],[path],[flags][,tag-file]]
+ */
+ char *disk_description, *tag_or_cab_file, *unused, *path;
+
+ ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &disk_description, ",");
+ if (!ok) {
+ continue;
+ }
+
+ ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &tag_or_cab_file, ",");
+ if (!ok) {
+ continue;
+ }
+
+ ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &unused, ",");
+ if (!ok) {
+ continue;
+ }
+
+ ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &path, ",");
+ if (!ok) {
+ continue;
+ }
+
+ *source_disk_name = path;
+
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_NOT_FOUND;
+}
+
+static NTSTATUS setup_driver_by_name(TALLOC_CTX *mem_ctx,
+ struct inf_context *inf_ctx,
+ const char *filename,
+ const char *environment,
+ const char *driver_name,
+ struct spoolss_AddDriverInfo8 *r,
+ const char **source_disk_name)
+{
+ NTSTATUS status;
+ struct gp_inifile_context *ctx = inf_ctx->ctx;
+ struct gp_inifile_context *core_ctx = inf_ctx->core_ctx;
+ char *key;
+ bool ok;
+ const char *short_environment;
+ const char *s;
+
+ short_environment = spoolss_get_short_filesys_environment(environment);
+ if (short_environment == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = find_driver_files(ctx, core_ctx, mem_ctx, driver_name, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = process_source_disk_name(ctx, mem_ctx,
+ short_environment,
+ source_disk_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->inf_path = talloc_strdup(mem_ctx, filename);
+ if (r->inf_path == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->architecture = talloc_strdup(mem_ctx, environment);
+ if (r->architecture == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (r->print_processor == NULL) {
+ r->print_processor = talloc_strdup(mem_ctx, "winprint");
+ if (r->print_processor == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ status = gp_inifile_getstring_ext(ctx, "Version:Class", &s);
+ if (NT_STATUS_IS_OK(status)) {
+ if (strequal(s, "Printer")) {
+ r->printer_driver_attributes |= PRINTER_DRIVER_CLASS;
+ }
+ }
+
+ status = gp_inifile_getstring(ctx, "Version:Signature", &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!strequal(s, "\"$Windows NT$\"")) {
+ return NT_STATUS_INVALID_SIGNATURE;
+ }
+
+ r->version = SPOOLSS_DRIVER_VERSION_200X;
+ status = gp_inifile_getstring(ctx, "Version:ClassVer", &s);
+ if (NT_STATUS_IS_OK(status)) {
+ int cmp = strncasecmp_m(s, "4.0", 3);
+ if (cmp == 0) {
+ r->version = SPOOLSS_DRIVER_VERSION_2012;
+ }
+ if (strequal(s, "3.0")) {
+ r->version = SPOOLSS_DRIVER_VERSION_200X;
+ }
+ }
+
+ status = gp_inifile_getstring_ext(ctx, "Version:Provider", &s);
+ if (NT_STATUS_IS_OK(status)) {
+ if (s != NULL) {
+ r->provider = talloc_strdup(mem_ctx, s);
+ if (r->provider == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ status = process_driver_driverver(ctx, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->printer_driver_attributes &= ~PRINTER_DRIVER_SANDBOX_ENABLED;
+
+ status = gp_inifile_getstring(ctx, "Version:DriverIsolation", &s);
+ if (NT_STATUS_IS_OK(status)) {
+ int cmp = strncasecmp_m(s, "2", 1);
+ if (cmp == 0) {
+ r->printer_driver_attributes |= PRINTER_DRIVER_SANDBOX_ENABLED;
+ }
+ cmp = strncasecmp_m(s, "0", 1);
+ if (cmp == 0) {
+ r->printer_driver_attributes &= ~PRINTER_DRIVER_SANDBOX_ENABLED;
+ }
+ }
+
+ status = find_manufacturer_name(ctx, mem_ctx, "Manufacturer", &r->manufacturer_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = find_manufacturer_url(ctx, mem_ctx, "OEM URLS", r->manufacturer_name, &r->manufacturer_url);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* not critical */
+ }
+
+ status = gp_inifile_getbool(ctx, "PrinterPackageInstallation:PackageAware", &ok);
+ if (NT_STATUS_IS_OK(status)) {
+ if (ok) {
+ r->printer_driver_attributes |= PRINTER_DRIVER_PACKAGE_AWARE;
+ }
+ }
+
+ key = talloc_asprintf(mem_ctx, "%s.%s:%s",
+ "PrinterPackageInstallation", short_environment, "PackageAware");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_getbool(ctx, key, &ok);
+ if (NT_STATUS_IS_OK(status)) {
+ if (ok) {
+ r->printer_driver_attributes |= PRINTER_DRIVER_PACKAGE_AWARE;
+ }
+ }
+
+ key = talloc_asprintf(mem_ctx, "%s.%s:%s",
+ "PrinterPackageInstallation", short_environment, "CoreDriverDependencies");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_getstring(ctx, key, &s);
+ if (NT_STATUS_IS_OK(status)) {
+ char **list;
+ r->core_driver_dependencies = talloc_zero(mem_ctx, struct spoolss_StringArray);
+ if (r->core_driver_dependencies == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ list = str_list_make_v3(r->core_driver_dependencies, s, ",");
+ if (list == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->core_driver_dependencies->string = const_str_list(list);
+ }
+
+ key = talloc_asprintf(mem_ctx, "%s.%s:%s",
+ "PrinterPackageInstallation", short_environment, "InboxVersionRequired");
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = gp_inifile_getstring(ctx, key, &s);
+ if (NT_STATUS_IS_OK(status)) {
+ if (strequal(s, "UseDriverVer")) {
+ r->min_inbox_driver_ver_date = r->driver_date;
+ r->min_inbox_driver_ver_version = r->driver_version;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+ parse the a printer inf file
+****************************************************************/
+
+NTSTATUS driver_inf_parse(TALLOC_CTX *mem_ctx,
+ const char *core_driver_inf,
+ const char *filename,
+ const char *environment,
+ const char *driver_name,
+ struct spoolss_AddDriverInfo8 *r,
+ const char **source_disk_name)
+{
+ NTSTATUS status;
+ struct inf_context *inf_ctx;
+
+ if (!filename || !environment) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = init_inf_context(mem_ctx,
+ filename,
+ core_driver_inf,
+ &inf_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = setup_driver_by_name(mem_ctx, inf_ctx,
+ filename,
+ environment,
+ driver_name,
+ r,
+ source_disk_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS driver_inf_list(TALLOC_CTX *mem_ctx,
+ const char *core_driver_inf,
+ const char *filename,
+ const char *environment,
+ uint32_t *count,
+ struct spoolss_AddDriverInfo8 **_r)
+{
+ NTSTATUS status;
+ const char *short_environment;
+ size_t d, num_devices = 0;
+ const char **devices = NULL;
+ const char **device_values = NULL;
+ struct inf_context *inf_ctx;
+
+ if (!filename || !environment) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ short_environment = spoolss_get_short_filesys_environment(environment);
+ if (short_environment == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = init_inf_context(mem_ctx,
+ filename,
+ core_driver_inf,
+ &inf_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = enum_devices_in_toc(inf_ctx->ctx, mem_ctx,
+ &num_devices,
+ &devices,
+ &device_values);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (d = 0; d < num_devices; d++) {
+
+ struct spoolss_AddDriverInfo8 r;
+ const char *source_disk_name;
+
+ ZERO_STRUCT(r);
+
+ status = setup_driver_by_name(mem_ctx, inf_ctx, filename,
+ environment, devices[d], &r,
+ &source_disk_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ADD_TO_ARRAY(mem_ctx, struct spoolss_AddDriverInfo8, r, _r, count);
+ }
+
+ return NT_STATUS_OK;
+}