Merge pull request #365 from bebehei/issue-322

Prevent replacement of format strings in message
This commit is contained in:
Nikos Tsipinakis 2017-09-09 09:08:50 +03:00 committed by GitHub
commit 29aa4c7ec8
7 changed files with 126 additions and 42 deletions

View File

@ -4,6 +4,7 @@
### Fixed ### Fixed
- `new_icon` rule being ignored on notifications that had a raw icon - `new_icon` rule being ignored on notifications that had a raw icon
- Do not replace format strings, which are in notification content
## Changed ## Changed
- transient hints are now handled - transient hints are now handled

View File

@ -324,6 +324,8 @@ equivalent notification attribute.
=item B<%n> progress value without any extra characters =item B<%n> progress value without any extra characters
=item B<%%> Literal %
=back =back
If any of these exists in the format but hasn't been specified in the If any of these exists in the format but hasn't been specified in the

View File

@ -123,6 +123,7 @@
# %I iconname (without its path) # %I iconname (without its path)
# %p progress value if set ([ 0%] to [100%]) or nothing # %p progress value if set ([ 0%] to [100%]) or nothing
# %n progress value if set without any extra characters # %n progress value if set without any extra characters
# %% Literal %
# Markup is allowed # Markup is allowed
format = "<b>%s</b>\n%b" format = "<b>%s</b>\n%b"

View File

@ -200,19 +200,31 @@ void notification_free(notification *n)
} }
/* /*
* Replace all occurrences of "needle" with a quoted "replacement", * Replace the two chars where **needle points
* according to the markup settings. * with a quoted "replacement", according to the markup settings.
*
* The needle is a double pointer and gets updated upon return
* to point to the first char, which occurs after replacement.
*
*/ */
char *notification_replace_format(const char *needle, const char *replacement, void notification_replace_single_field(char **haystack, char **needle,
char *haystack, enum markup_mode markup_mode) { const char *replacement, enum markup_mode markup_mode) {
char *tmp;
char *ret;
tmp = markup_transform(g_strdup(replacement), markup_mode); assert(*needle[0] == '%');
ret = string_replace_all(needle, tmp, haystack); // needle has to point into haystack (but not on the last char)
g_free(tmp); assert(*needle >= *haystack);
assert(*needle - *haystack < strlen(*haystack) - 1);
return ret; int pos = *needle - *haystack;
char *input = markup_transform(g_strdup(replacement), markup_mode);
*haystack = string_replace_at(*haystack, pos, 2, input);
// point the needle to the next char
// which was originally in haystack
*needle = *haystack + pos + strlen(input);
g_free(input);
} }
char *notification_extract_markup_urls(char **str_ptr) { char *notification_extract_markup_urls(char **str_ptr) {
@ -307,31 +319,89 @@ int notification_init(notification *n, int id)
n->urls = notification_extract_markup_urls(&(n->body)); n->urls = notification_extract_markup_urls(&(n->body));
n->msg = string_replace_all("\\n", "\n", g_strdup(n->format)); n->msg = string_replace_all("\\n", "\n", g_strdup(n->format));
n->msg = notification_replace_format("%a", n->appname, n->msg,
MARKUP_NO);
n->msg = notification_replace_format("%s", n->summary, n->msg,
n->markup);
n->msg = notification_replace_format("%b", n->body, n->msg,
n->markup);
if (n->icon) { /* replace all formatter */
char *tmp = g_strdup(n->icon); for(char *substr = strchr(n->msg, '%');
n->msg = notification_replace_format("%I", basename(tmp), substr;
n->msg, MARKUP_NO); substr = strchr(substr, '%')){
n->msg = notification_replace_format("%i", n->icon,
n->msg, MARKUP_NO);
g_free(tmp);
}
if (n->progress) { char pg[16];
char pg[10];
sprintf(pg, "[%3d%%]", n->progress - 1); switch(substr[1]){
n->msg = string_replace_all("%p", pg, n->msg); case 'a':
sprintf(pg, "%d", n->progress - 1); notification_replace_single_field(
n->msg = string_replace_all("%n", pg, n->msg); &n->msg,
} else { &substr,
n->msg = string_replace_all("%p", "", n->msg); n->appname,
n->msg = string_replace_all("%n", "", n->msg); MARKUP_NO);
break;
case 's':
notification_replace_single_field(
&n->msg,
&substr,
n->summary,
n->markup);
break;
case 'b':
notification_replace_single_field(
&n->msg,
&substr,
n->body,
n->markup);
break;
case 'I':
notification_replace_single_field(
&n->msg,
&substr,
n->icon ? basename(n->icon) : "",
MARKUP_NO);
break;
case 'i':
notification_replace_single_field(
&n->msg,
&substr,
n->icon ? n->icon : "",
MARKUP_NO);
break;
case 'p':
if (n->progress)
sprintf(pg, "[%3d%%]", n->progress - 1);
notification_replace_single_field(
&n->msg,
&substr,
n->progress ? pg : "",
MARKUP_NO);
break;
case 'n':
if (n->progress)
sprintf(pg, "%d", n->progress - 1);
notification_replace_single_field(
&n->msg,
&substr,
n->progress ? pg : "",
MARKUP_NO);
break;
case '%':
notification_replace_single_field(
&n->msg,
&substr,
"%",
MARKUP_NO);
break;
case '\0':
fprintf(stderr, "WARNING: format_string has trailing %% character."
"To escape it use %%%%.");
break;
default:
fprintf(stderr, "WARNING: format_string %%%c"
" is unknown\n", substr[1]);
// shift substr pointer forward,
// as we can't interpret the format string
substr++;
break;
}
} }
n->msg = g_strchomp(n->msg); n->msg = g_strchomp(n->msg);

View File

@ -73,7 +73,7 @@ int notification_is_duplicate(const notification *a, const notification *b);
void notification_run_script(notification *n); void notification_run_script(notification *n);
int notification_close(notification *n, int reason); int notification_close(notification *n, int reason);
void notification_print(notification *n); void notification_print(notification *n);
char *notification_replace_format(const char *needle, const char *replacement, char *haystack, enum markup_mode markup); void notification_replace_single_field(char **haystack, char **needle, const char *replacement, enum markup_mode markup_mode);
void notification_update_text_to_render(notification *n); void notification_update_text_to_render(notification *n);
int notification_get_ttl(notification *n); int notification_get_ttl(notification *n);
int notification_get_age(notification *n); int notification_get_age(notification *n);

View File

@ -9,6 +9,9 @@ char *string_replace_char(char needle, char replacement, char *haystack);
char *string_replace_all(const char *needle, const char *replacement, char *string_replace_all(const char *needle, const char *replacement,
char *haystack); char *haystack);
/* replace <len> characters with <repl> at position <pos> of the string <buf> */
char *string_replace_at(char *buf, int pos, int len, const char *repl);
/* replace needle with replacement in haystack */ /* replace needle with replacement in haystack */
char *string_replace(const char *needle, const char *replacement, char *string_replace(const char *needle, const char *replacement,
char *haystack); char *haystack);

View File

@ -70,21 +70,28 @@ TEST test_notification_is_duplicate(void *notifications)
PASS(); PASS();
} }
TEST test_notification_replace_format(void) TEST test_notification_replace_single_field(void)
{ {
char *str = g_malloc(128 * sizeof(char)); char *str = g_malloc(128 * sizeof(char));
char *substr = NULL;
strcpy(str, "Testing format replacement");
ASSERT_STR_EQ("Testing text replacement", (str = notification_replace_format("format", "text", str, MARKUP_FULL)));
strcpy(str, "Markup %a preserved"); strcpy(str, "Markup %a preserved");
ASSERT_STR_EQ("Markup and &amp; <i>is</i> preserved", (str = notification_replace_format("%a", "and &amp; <i>is</i>", str, MARKUP_FULL))); substr = strchr(str, '%');
notification_replace_single_field(&str, &substr, "and &amp; <i>is</i>", MARKUP_FULL);
ASSERT_STR_EQ("Markup and &amp; <i>is</i> preserved", str);
ASSERT_EQ(26, substr - str);
strcpy(str, "Markup %a escaped"); strcpy(str, "Markup %a escaped");
ASSERT_STR_EQ("Markup and &amp; &lt;i&gt;is&lt;/i&gt; escaped", (str = notification_replace_format("%a", "and & <i>is</i>", str, MARKUP_NO))); substr = strchr(str, '%');
notification_replace_single_field(&str, &substr, "and & <i>is</i>", MARKUP_NO);
ASSERT_STR_EQ("Markup and &amp; &lt;i&gt;is&lt;/i&gt; escaped", str);
ASSERT_EQ(38, substr - str);
strcpy(str, "Markup %a"); strcpy(str, "Markup %a");
ASSERT_STR_EQ("Markup is removed and &amp; escaped", (str = notification_replace_format("%a", "<i>is removed</i> and & escaped", str, MARKUP_STRIP))); substr = strchr(str, '%');
notification_replace_single_field(&str, &substr, "<i>is removed</i> and & escaped", MARKUP_STRIP);
ASSERT_STR_EQ("Markup is removed and &amp; escaped", str);
ASSERT_EQ(35, substr - str);
g_free(str); g_free(str);
PASS(); PASS();
@ -112,7 +119,7 @@ SUITE(suite_notification)
g_free(a); g_free(a);
g_free(b); g_free(b);
RUN_TEST(test_notification_replace_format); RUN_TEST(test_notification_replace_single_field);
} }
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */