msi: Rewrite command line parsing to handle quoted values correctly.

This commit is contained in:
Hans Leidekker 2010-12-23 17:07:55 +01:00 committed by Alexandre Julliard
parent 7bc7d09109
commit 489f82e9b4
2 changed files with 318 additions and 55 deletions

View file

@ -299,79 +299,167 @@ static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
msiobj_release(&row->hdr);
}
enum parse_state
{
state_whitespace,
state_token,
state_quote
};
static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
{
enum parse_state state = state_quote;
const WCHAR *p;
WCHAR *out = value;
int ignore, in_quotes = 0, count = 0, len = 0;
for (p = str; *p; p++)
{
ignore = 0;
switch (state)
{
case state_whitespace:
switch (*p)
{
case ' ':
if (!count) goto done;
in_quotes = 1;
ignore = 1;
break;
case '"':
state = state_quote;
if (in_quotes) count--;
else count++;
break;
default:
state = state_token;
if (!count) in_quotes = 0;
else in_quotes = 1;
len++;
break;
}
break;
case state_token:
switch (*p)
{
case '"':
state = state_quote;
if (in_quotes) count--;
else count++;
break;
case ' ':
state = state_whitespace;
if (!count) goto done;
in_quotes = 1;
break;
default:
if (!count) in_quotes = 0;
else in_quotes = 1;
len++;
break;
}
break;
case state_quote:
switch (*p)
{
case '"':
if (in_quotes) count--;
else count++;
break;
case ' ':
state = state_whitespace;
if (!count || !len) goto done;
in_quotes = 1;
break;
default:
state = state_token;
if (!count) in_quotes = 0;
else in_quotes = 1;
len++;
break;
}
break;
default: break;
}
if (!ignore) *out++ = *p;
}
done:
if (!len) *value = 0;
else *out = 0;
*quotes = count;
return p - str;
}
static void remove_quotes( WCHAR *str )
{
WCHAR *p = str;
int len = strlenW( str );
while ((p = strchrW( p, '"' )))
{
memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
p++;
}
}
UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
BOOL preserve_case )
{
LPCWSTR ptr,ptr2;
BOOL quote;
LPCWSTR ptr, ptr2;
int quotes;
DWORD len;
LPWSTR prop = NULL, val = NULL;
WCHAR *prop, *val;
UINT r;
if (!szCommandLine)
return ERROR_SUCCESS;
ptr = szCommandLine;
while (*ptr)
{
if (*ptr==' ')
{
ptr++;
continue;
}
while (*ptr == ' ') ptr++;
if (!*ptr) break;
TRACE("Looking at %s\n",debugstr_w(ptr));
ptr2 = strchrW(ptr,'=');
if (!ptr2)
{
ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
break;
}
ptr2 = strchrW( ptr, '=' );
if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
quote = FALSE;
len = ptr2 - ptr;
if (!len) return ERROR_INVALID_COMMAND_LINE;
len = ptr2-ptr;
prop = msi_alloc((len+1)*sizeof(WCHAR));
memcpy(prop,ptr,len*sizeof(WCHAR));
prop[len]=0;
if (!preserve_case)
struprW(prop);
prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
memcpy( prop, ptr, len * sizeof(WCHAR) );
prop[len] = 0;
if (!preserve_case) struprW( prop );
ptr2++;
len = 0;
ptr = ptr2;
while (*ptr && (quote || (!quote && *ptr!=' ')))
{
if (*ptr == '"')
quote = !quote;
ptr++;
len++;
}
if (*ptr2=='"')
{
ptr2++;
len -= 2;
}
val = msi_alloc((len+1)*sizeof(WCHAR));
memcpy(val,ptr2,len*sizeof(WCHAR));
val[len] = 0;
while (*ptr2 == ' ') ptr2++;
if (lstrlenW(prop) > 0)
quotes = 0;
val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
len = parse_prop( ptr2, val, &quotes );
if (quotes % 2)
{
UINT r = msi_set_property( package->db, prop, val );
TRACE("Found commandline property (%s) = (%s)\n",
debugstr_w(prop), debugstr_w(val));
if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
msi_reset_folders( package, TRUE );
WARN("unbalanced quotes\n");
msi_free( val );
msi_free( prop );
return ERROR_INVALID_COMMAND_LINE;
}
msi_free(val);
msi_free(prop);
remove_quotes( val );
TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
r = msi_set_property( package->db, prop, val );
if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
msi_reset_folders( package, TRUE );
msi_free( val );
msi_free( prop );
ptr = ptr2 + len;
}
return ERROR_SUCCESS;
@ -7356,7 +7444,9 @@ UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
msi_set_sourcedir_props(package, FALSE);
}
msi_parse_command_line( package, szCommandLine, FALSE );
rc = msi_parse_command_line( package, szCommandLine, FALSE );
if (rc != ERROR_SUCCESS)
return rc;
msi_apply_transforms( package );
msi_apply_patches( package );

View file

@ -1169,6 +1169,25 @@ static const CHAR sd_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComm
"TestSourceDirProp34\t19\t\tTest 34 failed\t\n"
"TestSourceDirProp35\t19\t\tTest 35 failed\t\n";
static const CHAR cl_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n"
"s72\ti2\tS64\tS0\tS255\n"
"CustomAction\tAction\n"
"TestCommandlineProp\t19\t\tTest1\t\n";
static const CHAR cl_install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
"s72\tS255\tI2\n"
"InstallExecuteSequence\tAction\n"
"LaunchConditions\t\t100\n"
"ValidateProductID\t\t700\n"
"CostInitialize\t\t800\n"
"FileCost\t\t900\n"
"CostFinalize\t\t1000\n"
"TestCommandlineProp\tP=\"one\"\t1100\n"
"InstallInitialize\t\t1500\n"
"ProcessComponents\t\t1600\n"
"InstallValidate\t\t1400\n"
"InstallFinalize\t\t5000\n";
typedef struct _msi_table
{
const CHAR *filename;
@ -1835,6 +1854,19 @@ static const msi_table pv_tables[] =
ADD_TABLE(property)
};
static const msi_table cl_tables[] =
{
ADD_TABLE(component),
ADD_TABLE(directory),
ADD_TABLE(feature),
ADD_TABLE(feature_comp),
ADD_TABLE(file),
ADD_TABLE(cl_custom_action),
ADD_TABLE(cl_install_exec_seq),
ADD_TABLE(media),
ADD_TABLE(property)
};
/* cabinet definitions */
/* make the max size large so there is only one cab file */
@ -6168,6 +6200,146 @@ error:
RemoveDirectory("msitest");
}
static void test_command_line_parsing(void)
{
UINT r;
const char *cmd;
if (is_process_limited())
{
skip("process is limited\n");
return;
}
create_test_files();
create_database(msifile, cl_tables, sizeof(cl_tables)/sizeof(msi_table));
MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
cmd = " ";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "=";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "==";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "one";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "=one";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P=";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = " P=";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P= ";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P=\"\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\"\"\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P=\"\"\"\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\" ";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P= \"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P= \"\" ";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=one";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
cmd = "P= one";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
cmd = "P=\"one";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P=one\"";
r = MsiInstallProductA(msifile, cmd);
todo_wine ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\"one\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
cmd = "P= \"one\" ";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
cmd = "P=\"one\"\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P=\"\"one\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P=\"\"one\"\"";
r = MsiInstallProductA(msifile, cmd);
todo_wine ok(r == ERROR_INVALID_COMMAND_LINE, "Expected ERROR_INVALID_COMMAND_LINE, got %u\n", r);
cmd = "P=\"one two\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\"\"\"one\"\" two\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\"\"\"one\"\" two\" Q=three";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\"\" Q=\"two\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
cmd = "P=\"one\" Q=\"two\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
cmd = "P=\"one=two\"";
r = MsiInstallProductA(msifile, cmd);
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
DeleteFile(msifile);
RemoveDirectory("msitest");
}
START_TEST(install)
{
DWORD len;
@ -6257,6 +6429,7 @@ START_TEST(install)
test_icon_table();
test_sourcedir_props();
test_package_validation();
test_command_line_parsing();
DeleteFileA(log_file);