mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
fe86989fcc
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
1256 lines
29 KiB
C
1256 lines
29 KiB
C
/*
|
|
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);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
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 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;
|
|
}
|