diff options
Diffstat (limited to 'pidgin/libpurple/protocols/facebook/json.c')
-rw-r--r-- | pidgin/libpurple/protocols/facebook/json.c | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/pidgin/libpurple/protocols/facebook/json.c b/pidgin/libpurple/protocols/facebook/json.c new file mode 100644 index 0000000..f36839c --- /dev/null +++ b/pidgin/libpurple/protocols/facebook/json.c @@ -0,0 +1,676 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" + +#include <stdarg.h> +#include <string.h> + +#include "json.h" +#include "util.h" + +typedef struct _FbJsonValue FbJsonValue; + +struct _FbJsonValue +{ + const gchar *expr; + FbJsonType type; + gboolean required; + GValue value; +}; + +struct _FbJsonValuesPrivate +{ + JsonNode *root; + GQueue *queue; + GList *next; + + gboolean isarray; + JsonArray *array; + guint index; + + GError *error; +}; + +G_DEFINE_TYPE(FbJsonValues, fb_json_values, G_TYPE_OBJECT); + +static void +fb_json_values_dispose(GObject *obj) +{ + FbJsonValue *value; + FbJsonValuesPrivate *priv = FB_JSON_VALUES(obj)->priv; + + while (!g_queue_is_empty(priv->queue)) { + value = g_queue_pop_head(priv->queue); + + if (G_IS_VALUE(&value->value)) { + g_value_unset(&value->value); + } + + g_free(value); + } + + if (priv->array != NULL) { + json_array_unref(priv->array); + } + + if (priv->error != NULL) { + g_error_free(priv->error); + } + + g_queue_free(priv->queue); +} + +static void +fb_json_values_class_init(FbJsonValuesClass *klass) +{ + GObjectClass *gklass = G_OBJECT_CLASS(klass); + + gklass->dispose = fb_json_values_dispose; + g_type_class_add_private(klass, sizeof (FbJsonValuesPrivate)); +} + +static void +fb_json_values_init(FbJsonValues *values) +{ + FbJsonValuesPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(values, FB_TYPE_JSON_VALUES, + FbJsonValuesPrivate); + values->priv = priv; + + priv->queue = g_queue_new(); +} + +GQuark +fb_json_error_quark(void) +{ + static GQuark q = 0; + + if (G_UNLIKELY(q == 0)) { + q = g_quark_from_static_string("fb-json-error-quark"); + } + + return q; +} + +JsonBuilder * +fb_json_bldr_new(JsonNodeType type) +{ + JsonBuilder *bldr; + + bldr = json_builder_new(); + + switch (type) { + case JSON_NODE_ARRAY: + fb_json_bldr_arr_begin(bldr, NULL); + break; + + case JSON_NODE_OBJECT: + fb_json_bldr_obj_begin(bldr, NULL); + break; + + default: + break; + } + + return bldr; +} + +gchar * +fb_json_bldr_close(JsonBuilder *bldr, JsonNodeType type, gsize *size) +{ + gchar *ret; + JsonGenerator *genr; + JsonNode *root; + + switch (type) { + case JSON_NODE_ARRAY: + fb_json_bldr_arr_end(bldr); + break; + + case JSON_NODE_OBJECT: + fb_json_bldr_obj_end(bldr); + break; + + default: + break; + } + + genr = json_generator_new(); + root = json_builder_get_root(bldr); + + json_generator_set_root(genr, root); + ret = json_generator_to_data(genr, size); + + json_node_free(root); + g_object_unref(genr); + g_object_unref(bldr); + + return ret; +} + +void +fb_json_bldr_arr_begin(JsonBuilder *bldr, const gchar *name) +{ + if (name != NULL) { + json_builder_set_member_name(bldr, name); + } + + json_builder_begin_array(bldr); +} + +void +fb_json_bldr_arr_end(JsonBuilder *bldr) +{ + json_builder_end_array(bldr); +} + +void +fb_json_bldr_obj_begin(JsonBuilder *bldr, const gchar *name) +{ + if (name != NULL) { + json_builder_set_member_name(bldr, name); + } + + json_builder_begin_object(bldr); +} + +void +fb_json_bldr_obj_end(JsonBuilder *bldr) +{ + json_builder_end_object(bldr); +} + +void +fb_json_bldr_add_bool(JsonBuilder *bldr, const gchar *name, gboolean value) +{ + if (name != NULL) { + json_builder_set_member_name(bldr, name); + } + + json_builder_add_boolean_value(bldr, value); +} + +void +fb_json_bldr_add_dbl(JsonBuilder *bldr, const gchar *name, gdouble value) +{ + if (name != NULL) { + json_builder_set_member_name(bldr, name); + } + + json_builder_add_double_value(bldr, value); +} + +void +fb_json_bldr_add_int(JsonBuilder *bldr, const gchar *name, gint64 value) +{ + if (name != NULL) { + json_builder_set_member_name(bldr, name); + } + + json_builder_add_int_value(bldr, value); +} + +void +fb_json_bldr_add_str(JsonBuilder *bldr, const gchar *name, const gchar *value) +{ + if (name != NULL) { + json_builder_set_member_name(bldr, name); + } + + json_builder_add_string_value(bldr, value); +} + +void +fb_json_bldr_add_strf(JsonBuilder *bldr, const gchar *name, + const gchar *format, ...) +{ + gchar *value; + va_list ap; + + va_start(ap, format); + value = g_strdup_vprintf(format, ap); + va_end(ap); + + fb_json_bldr_add_str(bldr, name, value); + g_free(value); +} + +JsonNode * +fb_json_node_new(const gchar *data, gssize size, GError **error) +{ + gchar *slice; + JsonNode *root; + JsonParser *prsr; + + g_return_val_if_fail(data != NULL, NULL); + + if (size < 0) { + size = strlen(data); + } + + /* Ensure data is null terminated for json-glib < 1.0.2 */ + slice = g_strndup(data, size); + prsr = json_parser_new(); + + if (!json_parser_load_from_data(prsr, slice, size, error)) { + g_object_unref(prsr); + g_free(slice); + return NULL; + } + + root = json_parser_get_root(prsr); + root = json_node_copy(root); + + g_object_unref(prsr); + g_free(slice); + return root; +} + +JsonNode * +fb_json_node_get(JsonNode *root, const gchar *expr, GError **error) +{ + GError *err = NULL; + guint size; + JsonArray *rslt; + JsonNode *node; + JsonNode *ret; + + /* Special case for json-glib < 0.99.2 */ + if (purple_strequal(expr, "$")) { + return json_node_copy(root); + } + + node = json_path_query(expr, root, &err); + + if (err != NULL) { + g_propagate_error(error, err); + json_node_free(node); + return NULL; + } + + rslt = json_node_get_array(node); + size = json_array_get_length(rslt); + + if (size < 1) { + g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_NOMATCH, + _("No matches for %s"), expr); + json_node_free(node); + return NULL; + } + + if (size > 1) { + g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_AMBIGUOUS, + _("Ambiguous matches for %s"), expr); + json_node_free(node); + return NULL; + } + + if (json_array_get_null_element(rslt, 0)) { + g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_NULL, + _("Null value for %s"), expr); + json_node_free(node); + return NULL; + } + + ret = json_array_dup_element(rslt, 0); + json_node_free(node); + return ret; +} + +JsonNode * +fb_json_node_get_nth(JsonNode *root, guint n) +{ + GList *vals; + JsonNode *ret; + JsonObject *obj; + + obj = json_node_get_object(root); + vals = json_object_get_values(obj); + ret = g_list_nth_data(vals, n); + + g_list_free(vals); + return ret; +} + +JsonArray * +fb_json_node_get_arr(JsonNode *root, const gchar *expr, GError **error) +{ + JsonArray *ret; + JsonNode *rslt; + + rslt = fb_json_node_get(root, expr, error); + + if (rslt == NULL) { + return NULL; + } + + ret = json_node_dup_array(rslt); + json_node_free(rslt); + return ret; +} + +gboolean +fb_json_node_get_bool(JsonNode *root, const gchar *expr, GError **error) +{ + gboolean ret; + JsonNode *rslt; + + rslt = fb_json_node_get(root, expr, error); + + if (rslt == NULL) { + return FALSE; + } + + ret = json_node_get_boolean(rslt); + json_node_free(rslt); + return ret; +} + +gdouble +fb_json_node_get_dbl(JsonNode *root, const gchar *expr, GError **error) +{ + gdouble ret; + JsonNode *rslt; + + rslt = fb_json_node_get(root, expr, error); + + if (rslt == NULL) { + return 0.0; + } + + ret = json_node_get_double(rslt); + json_node_free(rslt); + return ret; +} + +gint64 +fb_json_node_get_int(JsonNode *root, const gchar *expr, GError **error) +{ + gint64 ret; + JsonNode *rslt; + + rslt = fb_json_node_get(root, expr, error); + + if (rslt == NULL) { + return 0; + } + + ret = json_node_get_int(rslt); + json_node_free(rslt); + return ret; +} + +gchar * +fb_json_node_get_str(JsonNode *root, const gchar *expr, GError **error) +{ + gchar *ret; + JsonNode *rslt; + + rslt = fb_json_node_get(root, expr, error); + + if (rslt == NULL) { + return NULL; + } + + ret = json_node_dup_string(rslt); + json_node_free(rslt); + return ret; +} + +FbJsonValues * +fb_json_values_new(JsonNode *root) +{ + FbJsonValues *values; + FbJsonValuesPrivate *priv; + + g_return_val_if_fail(root != NULL, NULL); + + values = g_object_new(FB_TYPE_JSON_VALUES, NULL); + priv = values->priv; + priv->root = root; + + return values; +} + +void +fb_json_values_add(FbJsonValues *values, FbJsonType type, gboolean required, + const gchar *expr) +{ + FbJsonValue *value; + FbJsonValuesPrivate *priv; + + g_return_if_fail(values != NULL); + g_return_if_fail(expr != NULL); + priv = values->priv; + + value = g_new0(FbJsonValue, 1); + value->expr = expr; + value->type = type; + value->required = required; + + g_queue_push_tail(priv->queue, value); +} + +JsonNode * +fb_json_values_get_root(FbJsonValues *values) +{ + FbJsonValuesPrivate *priv; + guint index; + + g_return_val_if_fail(values != NULL, NULL); + priv = values->priv; + + if (priv->array == NULL) { + return priv->root; + } + + g_return_val_if_fail(priv->index > 0, NULL); + index = priv->index - 1; + + if (json_array_get_length(priv->array) <= index) { + return NULL; + } + + return json_array_get_element(priv->array, index); +} + +void +fb_json_values_set_array(FbJsonValues *values, gboolean required, + const gchar *expr) +{ + FbJsonValuesPrivate *priv; + + g_return_if_fail(values != NULL); + priv = values->priv; + + priv->array = fb_json_node_get_arr(priv->root, expr, &priv->error); + priv->isarray = TRUE; + + if ((priv->error != NULL) && !required) { + g_clear_error(&priv->error); + } +} + +gboolean +fb_json_values_update(FbJsonValues *values, GError **error) +{ + FbJsonValue *value; + FbJsonValuesPrivate *priv; + GError *err = NULL; + GList *l; + GType type; + JsonNode *root; + JsonNode *node; + + g_return_val_if_fail(values != NULL, FALSE); + priv = values->priv; + + if (G_UNLIKELY(priv->error != NULL)) { + g_propagate_error(error, priv->error); + priv->error = NULL; + return FALSE; + } + + if (priv->isarray) { + if ((priv->array == NULL) || + (json_array_get_length(priv->array) <= priv->index)) + { + return FALSE; + } + + root = json_array_get_element(priv->array, priv->index++); + } else { + root = priv->root; + } + + g_return_val_if_fail(root != NULL, FALSE); + + for (l = priv->queue->head; l != NULL; l = l->next) { + value = l->data; + node = fb_json_node_get(root, value->expr, &err); + + if (G_IS_VALUE(&value->value)) { + g_value_unset(&value->value); + } + + if (err != NULL) { + json_node_free(node); + + if (value->required) { + g_propagate_error(error, err); + return FALSE; + } + + g_clear_error(&err); + continue; + } + + type = json_node_get_value_type(node); + + if (G_UNLIKELY(type != value->type)) { + g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_TYPE, + _("Expected a %s but got a %s for %s"), + g_type_name(value->type), + g_type_name(type), + value->expr); + json_node_free(node); + return FALSE; + } + + json_node_get_value(node, &value->value); + json_node_free(node); + } + + priv->next = priv->queue->head; + return TRUE; +} + +const GValue * +fb_json_values_next(FbJsonValues *values) +{ + FbJsonValue *value; + FbJsonValuesPrivate *priv; + + g_return_val_if_fail(values != NULL, NULL); + priv = values->priv; + + g_return_val_if_fail(priv->next != NULL, NULL); + value = priv->next->data; + priv->next = priv->next->next; + + if (!G_IS_VALUE(&value->value)) { + return NULL; + } + + return &value->value; +} + +gboolean +fb_json_values_next_bool(FbJsonValues *values, gboolean defval) +{ + const GValue *value; + + value = fb_json_values_next(values); + + if (G_UNLIKELY(value == NULL)) { + return defval; + } + + return g_value_get_boolean(value); +} + +gdouble +fb_json_values_next_dbl(FbJsonValues *values, gdouble defval) +{ + const GValue *value; + + value = fb_json_values_next(values); + + if (G_UNLIKELY(value == NULL)) { + return defval; + } + + return g_value_get_double(value); +} + +gint64 +fb_json_values_next_int(FbJsonValues *values, gint64 defval) +{ + const GValue *value; + + value = fb_json_values_next(values); + + if (G_UNLIKELY(value == NULL)) { + return defval; + } + + return g_value_get_int64(value); +} + +const gchar * +fb_json_values_next_str(FbJsonValues *values, const gchar *defval) +{ + const GValue *value; + + value = fb_json_values_next(values); + + if (G_UNLIKELY(value == NULL)) { + return defval; + } + + return g_value_get_string(value); +} + +gchar * +fb_json_values_next_str_dup(FbJsonValues *values, const gchar *defval) +{ + const GValue *value; + + value = fb_json_values_next(values); + + if (G_UNLIKELY(value == NULL)) { + return g_strdup(defval); + } + + return g_value_dup_string(value); +} |