Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion design-data/ical-properties.csv
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
#"NAME","115","TEXT","TEXT" (conflicts with NAME from rfc4324#section-2.1.2)
"REFRESH-INTERVAL","116","DURATION","NO"
"SOURCE","117","URI","NO"
"COLOR","118","TEXT","TEXT"
"COLOR","118","COLOR","COLOR"
"IMAGE","119","ATTACH","NO",is_structured
"CONFERENCE","120","URI","NO"

Expand Down
3 changes: 3 additions & 0 deletions design-data/ical-value-types.csv
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@
"LINK","5042","(m)const char*","string","UID;URI;XML-REFERENCE",
"RELATED-TO","5043","(m)const char*","string","TEXT;UID;URI",

"#RFC 7986 Section 5.9","Updated by draft-ietf-calext-icalendar-jscalendar-extensions Section 2.2",,,
"COLOR","5044","(m)const char*","string","unitary",

"#NOTE for updaters. Preserve the icalvalue_kind Enum values and property Enum values to aid forward compatibility"
"# New Enum values for an existing icalvalue_kind should be inserted before the corresponding NONE value"
"# New icalvalue_kind types should start their Enum value after the highest NONE value (currently 11599)"
228 changes: 228 additions & 0 deletions src/libical/icalderivedvalue.c.in
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,234 @@ struct icalreqstattype icalvalue_get_requeststatus(const icalvalue *value)
return ((struct icalvalue_impl *)value)->data.v_requeststatus;
}

/*
* Color names from Section 4.3 of https://www.w3.org/TR/css-color-3/
*
* MUST keep sorted for bsearch()
*/
static const char *css_color_names[] = {
"aliceblue",
"antiquewhite",
"aqua",
"aquamarine",
"azure",
"beige",
"bisque",
"black",
"blanchedalmond",
"blue",
"blueviolet",
"brown",
"burlywood",
"cadetblue",
"chartreuse",
"chocolate",
"coral",
"cornflowerblue",
"cornsilk",
"crimson",
"cyan",
"darkblue",
"darkcyan",
"darkgoldenrod",
"darkgray",
"darkgreen",
"darkgrey",
"darkkhaki",
"darkmagenta",
"darkolivegreen",
"darkorange",
"darkorchid",
"darkred",
"darksalmon",
"darkseagreen",
"darkslateblue",
"darkslategray",
"darkslategrey",
"darkturquoise",
"darkviolet",
"deeppink",
"deepskyblue",
"dimgray",
"dimgrey",
"dodgerblue",
"firebrick",
"floralwhite",
"forestgreen",
"fuchsia",
"gainsboro",
"ghostwhite",
"gold",
"goldenrod",
"gray",
"green",
"greenyellow",
"grey",
"honeydew",
"hotpink",
"indianred",
"indigo",
"ivory",
"khaki",
"lavender",
"lavenderblush",
"lawngreen",
"lemonchiffon",
"lightblue",
"lightcoral",
"lightcyan",
"lightgoldenrodyellow",
"lightgray",
"lightgreen",
"lightgrey",
"lightpink",
"lightsalmon",
"lightseagreen",
"lightskyblue",
"lightslategray",
"lightslategrey",
"lightsteelblue",
"lightyellow",
"lime",
"limegreen",
"linen",
"magenta",
"maroon",
"mediumaquamarine",
"mediumblue",
"mediumorchid",
"mediumpurple",
"mediumseagreen",
"mediumslateblue",
"mediumspringgreen",
"mediumturquoise",
"mediumvioletred",
"midnightblue",
"mintcream",
"mistyrose",
"moccasin",
"navajowhite",
"navy",
"oldlace",
"olive",
"olivedrab",
"orange",
"orangered",
"orchid",
"palegoldenrod",
"palegreen",
"paleturquoise",
"palevioletred",
"papayawhip",
"peachpuff",
"peru",
"pink",
"plum",
"powderblue",
"purple",
"rebeccapurple", /* Added by CSS Color 4 */
"red",
"rosybrown",
"royalblue",
"saddlebrown",
"salmon",
"sandybrown",
"seagreen",
"seashell",
"sienna",
"silver",
"skyblue",
"slateblue",
"slategray",
"slategrey",
"snow",
"springgreen",
"steelblue",
"tan",
"teal",
"thistle",
"tomato",
"turquoise",
"violet",
"wheat",
"white",
"whitesmoke",
"yellow",
"yellowgreen",
};

static int strpcasecmp(const void *a, const void *b)
{
const char *s1 = *(const char **)a;
const char *s2 = *(const char **)b;

return strcasecmp(s1, s2);
}

/* The value is a color name taken from the set of names defined in
Section 4.3 of https://www.w3.org/TR/css-color-3/
or an RGB value in six-digit hexadecimal notation,
as defined in Section 4.2.1 of https://www.w3.org/TR/css-color-3/
Values are case-insensitive.
*/
static bool is_css_color(const char *v)
{
if (v[0] == '#') {
/* 6-digit hex */
return (strspn(v+1, "0123456789abcdefABCDEF") == 6 && v[7] == '\0');
}
else {
size_t size = sizeof(css_color_names[0]);
size_t nmemb = sizeof(css_color_names) / size;

return (bsearch(&v, css_color_names, nmemb, size, &strpcasecmp) != 0);
}
}

icalvalue *icalvalue_new_color(const char * v)
{
struct icalvalue_impl *impl;
if (!v || !is_css_color(v)) {
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return 0;
}

impl = icalvalue_new_impl(ICAL_COLOR_VALUE);
icalvalue_set_color((icalvalue *)impl, v);
return (icalvalue*)impl;
}

void icalvalue_set_color(icalvalue *value, const char * v)
{
struct icalvalue_impl *impl;
icalerror_check_arg_rv((value != 0), "value");
if (!v || !is_css_color(v)) {
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return;
}

icalerror_check_value_type(value, ICAL_COLOR_VALUE);
impl = (struct icalvalue_impl *)value;
if (impl->data.v_string != 0) {
icalmemory_free_buffer((void *)impl->data.v_string);
}

impl->data.v_string = icalmemory_strdup(v);

if (impl->data.v_string == 0) {
errno = ENOMEM;
}

icalvalue_reset_kind(impl);
}

const char * icalvalue_get_color(const icalvalue *value)
{
icalerror_check_arg_rz((value != 0), "value");
icalerror_check_value_type(value, ICAL_COLOR_VALUE);
return (((struct icalvalue_impl *)value)->data.v_string);
}

/* The remaining interfaces are 'new', 'set' and 'get' for each of the value
types */

Expand Down
4 changes: 4 additions & 0 deletions src/libical/icalderivedvalue.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ LIBICAL_ICAL_EXPORT icalvalue *icalvalue_new_requeststatus(struct icalreqstattyp
LIBICAL_ICAL_EXPORT struct icalreqstattype icalvalue_get_requeststatus(const icalvalue *value);
LIBICAL_ICAL_EXPORT void icalvalue_set_requeststatus(icalvalue *value, struct icalreqstattype v);

LIBICAL_ICAL_EXPORT icalvalue *icalvalue_new_color(const char *v);
LIBICAL_ICAL_EXPORT void icalvalue_set_color(icalvalue *value, const char *v);
LIBICAL_ICAL_EXPORT const char *icalvalue_get_color(const icalvalue *value);

<insert_code_here>

LIBICAL_ICAL_EXPORT icalvalue *icalvalue_new_class(enum icalproperty_class v);
Expand Down
10 changes: 10 additions & 0 deletions src/libical/icalvalue.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ icalvalue *icalvalue_clone(const icalvalue *old)
case ICAL_QUERY_VALUE:
case ICAL_STRING_VALUE:
case ICAL_TEXT_VALUE:
case ICAL_COLOR_VALUE:
case ICAL_CALADDRESS_VALUE:
case ICAL_UID_VALUE:
case ICAL_XMLREFERENCE_VALUE:
Expand Down Expand Up @@ -588,6 +589,10 @@ static icalvalue *icalvalue_new_from_string_with_error(icalvalue_kind kind,
value = icalvalue_new_string(str);
break;

case ICAL_COLOR_VALUE:
value = icalvalue_new_color(str);
break;

case ICAL_CALADDRESS_VALUE:
value = icalvalue_new_caladdress(str);
break;
Expand Down Expand Up @@ -811,6 +816,9 @@ void icalvalue_free(icalvalue *v)
case ICAL_CALADDRESS_VALUE:
_fallthrough();

case ICAL_COLOR_VALUE:
_fallthrough();

case ICAL_URI_VALUE:
_fallthrough();

Expand Down Expand Up @@ -1235,6 +1243,7 @@ char *icalvalue_as_ical_string_r(const icalvalue *value)

case ICAL_STRING_VALUE:
case ICAL_URI_VALUE:
case ICAL_COLOR_VALUE:
case ICAL_CALADDRESS_VALUE:
case ICAL_XMLREFERENCE_VALUE:
return icalvalue_string_as_ical_string_r(value);
Expand Down Expand Up @@ -1434,6 +1443,7 @@ icalparameter_xliccomparetype icalvalue_compare(const icalvalue *a, const icalva

case ICAL_TEXT_VALUE:
case ICAL_URI_VALUE:
case ICAL_COLOR_VALUE:
case ICAL_CALADDRESS_VALUE:
case ICAL_TRIGGER_VALUE:
case ICAL_DATE_VALUE:
Expand Down
30 changes: 30 additions & 0 deletions src/test/regression.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,37 @@ void test_values(void)

if (v!=0) icalvalue_free(v); */

v = icalvalue_new_color("red");
str_is("icalvalue_new_color(red)", icalvalue_get_color(v), "red");
icalvalue_set_color(v, "Blue");
str_is("icalvalue_set_color(Blue)", icalvalue_get_color(v), "Blue");

icalvalue_set_color(v, "#abcdef");
str_is("icalvalue_set_color(#abcdef)", icalvalue_get_color(v), "#abcdef");
icalvalue_set_color(v, "#ABCDEF");
str_is("icalvalue_set_color(#ABCDEF)", icalvalue_get_color(v), "#ABCDEF");
str_is("icalvalue_as_ical_string()", icalvalue_as_ical_string(v), "#ABCDEF");

copy = icalvalue_clone(v);
str_is("icalvalue_clone()", icalvalue_as_ical_string(copy), "#ABCDEF");
icalvalue_free(copy);

icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL);

/* The following set should fail, leaving the previous value */
icalvalue_set_color(v, "Gonk");
str_is("icalvalue_set_color(Gonk)", icalvalue_get_color(v), "#ABCDEF");
icalvalue_free(v);

v = icalvalue_new_from_string(ICAL_COLOR_VALUE, "Gonk");
ok("illegal color value 'Gonk'", (v == 0));
v = icalvalue_new_from_string(ICAL_COLOR_VALUE, "#Gonk");
ok("illegal color value '#Gonk'", (v == 0));
v = icalvalue_new_from_string(ICAL_COLOR_VALUE, "#00");
ok("illegal color value '#00'", (v == 0));
v = icalvalue_new_from_string(ICAL_COLOR_VALUE, "#00000000");
ok("illegal color value '#00000000'", (v == 0));

v = icalvalue_new_from_string(ICAL_RECUR_VALUE, "D2 #0");
ok("illegal recur value", (v == 0));

Expand Down
Loading