/* * Construct an iCalendar property value from XML content. */ static icalvalue *xml_element_to_icalvalue(xmlNodePtr xtype, icalvalue_kind kind) { icalvalue *value = NULL; xmlNodePtr node; xmlChar *content = NULL; switch (kind) { case ICAL_GEO_VALUE: { struct icalgeotype geo; node = xmlFirstElementChild(xtype); if (!node) { syslog(LOG_WARNING, "Missing <latitude> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "latitude")) { syslog(LOG_WARNING, "Expected <latitude> XML element, received %s", node->name); break; } content = xmlNodeGetContent(node); geo.lat = atof((const char *) content); node = xmlNextElementSibling(node); if (!node) { syslog(LOG_WARNING, "Missing <longitude> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "longitude")) { syslog(LOG_WARNING, "Expected <longitude> XML element, received %s", node->name); break; } xmlFree(content); content = xmlNodeGetContent(node); geo.lon = atof((const char *) content); value = icalvalue_new_geo(geo); break; } case ICAL_PERIOD_VALUE: { struct icalperiodtype p; p.start = p.end = icaltime_null_time(); p.duration = icaldurationtype_from_int(0); node = xmlFirstElementChild(xtype); if (!node) { syslog(LOG_WARNING, "Missing <start> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "start")) { syslog(LOG_WARNING, "Expected <start> XML element, received %s", node->name); break; } content = xmlNodeGetContent(node); p.start = icaltime_from_string((const char *) content); if (icaltime_is_null_time(p.start)) break; node = xmlNextElementSibling(node); if (!node) { syslog(LOG_WARNING, "Missing <end> / <duration> XML element"); break; } else if (!xmlStrcmp(node->name, BAD_CAST "end")) { xmlFree(content); content = xmlNodeGetContent(node); p.end = icaltime_from_string((const char *) content); if (icaltime_is_null_time(p.end)) break; } else if (!xmlStrcmp(node->name, BAD_CAST "duration")) { xmlFree(content); content = xmlNodeGetContent(node); p.duration = icaldurationtype_from_string((const char *) content); if (icaldurationtype_as_int(p.duration) == 0) break; } else { syslog(LOG_WARNING, "Expected <end> / <duration> XML element, received %s", node->name); break; } value = icalvalue_new_period(p); break; } case ICAL_RECUR_VALUE: { struct buf rrule = BUF_INITIALIZER; struct hash_table byrules; struct icalrecurrencetype rt; char *sep = ""; construct_hash_table(&byrules, 10, 1); /* create an iCal RRULE string from xCal <recur> sub-elements */ for (node = xmlFirstElementChild(xtype); node; node = xmlNextElementSibling(node)) { content = xmlNodeGetContent(node); if (!xmlStrncmp(node->name, BAD_CAST "by", 2)) { /* BY* rules can have a list of values - assemble them using a hash table */ struct buf *vals = hash_lookup((const char *) node->name, &byrules); if (vals) { /* append this value to existing list */ buf_printf(vals, ",%s", (char *) content); } else { /* create new list with this valiue */ vals = xzmalloc(sizeof(struct buf)); buf_setcstr(vals, (char *) content); hash_insert((char *) node->name, vals, &byrules); } } else { /* single value rpart */ buf_printf(&rrule, "%s%s=%s", sep, ucase((char *) node->name), (char *) content); sep = ";"; } xmlFree(content); content = NULL; } /* append the BY* rules to RRULE buffer */ hash_enumerate(&byrules, (void (*)(const char*, void*, void*)) &append_byrule, &rrule); free_hash_table(&byrules, NULL); /* parse our iCal RRULE string */ rt = icalrecurrencetype_from_string(buf_cstring(&rrule)); buf_free(&rrule); if (rt.freq != ICAL_NO_RECURRENCE) value = icalvalue_new_recur(rt); break; } case ICAL_REQUESTSTATUS_VALUE: { struct icalreqstattype rst = { ICAL_UNKNOWN_STATUS, NULL, NULL }; short maj, min; node = xmlFirstElementChild(xtype); if (!node) { syslog(LOG_WARNING, "Missing <code> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "code")) { syslog(LOG_WARNING, "Expected <code> XML element, received %s", node->name); break; } content = xmlNodeGetContent(node); if (sscanf((const char *) content, "%hd.%hd", &maj, &min) == 2) { rst.code = icalenum_num_to_reqstat(maj, min); } if (rst.code == ICAL_UNKNOWN_STATUS) { syslog(LOG_WARNING, "Unknown request-status code"); break; } node = xmlNextElementSibling(node); if (!node) { syslog(LOG_WARNING, "Missing <description> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "description")) { syslog(LOG_WARNING, "Expected <description> XML element, received %s", node->name); break; } xmlFree(content); content = xmlNodeGetContent(node); rst.desc = (const char *) content; node = xmlNextElementSibling(node); if (node) { if (xmlStrcmp(node->name, BAD_CAST "data")) { syslog(LOG_WARNING, "Expected <data> XML element, received %s", node->name); break; } xmlFree(content); content = xmlNodeGetContent(node); rst.debug = (const char *) content; } value = icalvalue_new_requeststatus(rst); break; } case ICAL_UTCOFFSET_VALUE: { int n, utcoffset, hours, minutes, seconds = 0; char sign; content = xmlNodeGetContent(xtype); n = sscanf((const char *) content, "%c%02d:%02d:%02d", &sign, &hours, &minutes, &seconds); if (n < 3) { syslog(LOG_WARNING, "Unexpected utc-offset format"); break; } utcoffset = hours*3600 + minutes*60 + seconds; if (sign == '-') utcoffset = -utcoffset; value = icalvalue_new_utcoffset(utcoffset); break; } default: content = xmlNodeGetContent(xtype); value = icalvalue_new_from_string(kind, (const char *) content); break; } if (content) xmlFree(content); return value; }
/* * Construct an iCalendar property value from a JSON object. */ static icalvalue *json_object_to_icalvalue(json_t *jvalue, icalvalue_kind kind) { icalvalue *value = NULL; int len, i; switch (kind) { case ICAL_BOOLEAN_VALUE: if (json_is_boolean(jvalue)) value = icalvalue_new_integer(json_is_true(jvalue)); else syslog(LOG_WARNING, "jCal boolean object expected"); break; case ICAL_FLOAT_VALUE: if (json_is_real(jvalue)) value = icalvalue_new_float((float) json_real_value(jvalue)); else syslog(LOG_WARNING, "jCal double object expected"); break; case ICAL_GEO_VALUE: /* MUST be an array of 2 doubles */ if (json_is_array(jvalue) && (len = json_array_size(jvalue)) != 2) { for (i = 0; i < len && json_is_real(json_array_get(jvalue, i)); i++); if (i == len) { struct icalgeotype geo; geo.lat = json_real_value(json_array_get(jvalue, 0)); geo.lon = json_real_value(json_array_get(jvalue, 1)); value = icalvalue_new_geo(geo); } } if (!value) syslog(LOG_WARNING, "jCal array object of 2 doubles expected"); break; case ICAL_INTEGER_VALUE: if (json_is_integer(jvalue)) value = icalvalue_new_integer((int) json_integer_value(jvalue)); else if (json_is_string(jvalue)) value = icalvalue_new_integer(atoi(json_string_value(jvalue))); else syslog(LOG_WARNING, "jCal integer object expected"); break; case ICAL_RECUR_VALUE: if (json_is_object(jvalue)) { struct buf rrule = BUF_INITIALIZER; struct icalrecurrencetype rt; const char *key, *sep = ""; json_t *val; /* create an iCal RRULE string from jCal 'recur' object */ json_object_foreach(jvalue, key, val) { char *mykey = xstrdup(key); buf_printf(&rrule, "%s%s=", sep, ucase(mykey)); buf_appendjson(&rrule, val); sep = ";"; free(mykey); } /* parse our iCal RRULE string */ rt = icalrecurrencetype_from_string(buf_cstring(&rrule)); buf_free(&rrule); if (rt.freq != ICAL_NO_RECURRENCE) value = icalvalue_new_recur(rt); } else