eion/purple-hangouts
* Hangouts Plugin for libpurple/Pidgin * Copyright (c) 2015-2016 Eion Robb, Mike Ruprecht * 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 3 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, see <http://www.gnu.org/licenses/>. #include "hangouts_pblite.h" static void *glib_protobufc_allocator_alloc(void *allocator_data, size_t size) { return g_try_malloc0(size); }; static void glib_protobufc_allocator_free(void *allocator_data, void *pointer) { g_free(pointer); }; static ProtobufCAllocator glib_protobufc_allocator = { .alloc = &glib_protobufc_allocator_alloc, .free = &glib_protobufc_allocator_free, * Given a field type, return the in-memory size. * \todo Implement as a table lookup. sizeof_elt_in_repeated_array(ProtobufCType type) case PROTOBUF_C_TYPE_SINT32: case PROTOBUF_C_TYPE_INT32: case PROTOBUF_C_TYPE_UINT32: case PROTOBUF_C_TYPE_SFIXED32: case PROTOBUF_C_TYPE_FIXED32: case PROTOBUF_C_TYPE_FLOAT: case PROTOBUF_C_TYPE_ENUM: case PROTOBUF_C_TYPE_SINT64: case PROTOBUF_C_TYPE_INT64: case PROTOBUF_C_TYPE_UINT64: case PROTOBUF_C_TYPE_SFIXED64: case PROTOBUF_C_TYPE_FIXED64: case PROTOBUF_C_TYPE_DOUBLE: case PROTOBUF_C_TYPE_BOOL: return sizeof(protobuf_c_boolean); case PROTOBUF_C_TYPE_STRING: case PROTOBUF_C_TYPE_MESSAGE: case PROTOBUF_C_TYPE_BYTES: return sizeof(ProtobufCBinaryData); g_return_val_if_reached(0); #define STRUCT_MEMBER_P(struct_p, struct_offset) \ ((void *) ((uint8_t *) (struct_p) + (struct_offset))) #define STRUCT_MEMBER_PTR(member_type, struct_p, struct_offset) \ ((member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset))) #define STRUCT_MEMBER(member_type, struct_p, struct_offset) \ (*(member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset))) pblite_decode_field(const ProtobufCFieldDescriptor *field, JsonNode *value, gpointer member) case PROTOBUF_C_TYPE_INT32: case PROTOBUF_C_TYPE_UINT32: case PROTOBUF_C_TYPE_SFIXED32: case PROTOBUF_C_TYPE_FIXED32: case PROTOBUF_C_TYPE_FLOAT: case PROTOBUF_C_TYPE_ENUM: *(uint32_t *) member = json_node_get_int(value); case PROTOBUF_C_TYPE_SINT32: *(int32_t *) member = json_node_get_int(value); case PROTOBUF_C_TYPE_INT64: case PROTOBUF_C_TYPE_UINT64: case PROTOBUF_C_TYPE_SFIXED64: case PROTOBUF_C_TYPE_FIXED64: case PROTOBUF_C_TYPE_DOUBLE: *(uint64_t *) member = json_node_get_int(value); case PROTOBUF_C_TYPE_SINT64: *(int64_t *) member = json_node_get_int(value); case PROTOBUF_C_TYPE_BOOL: *(protobuf_c_boolean *) member = json_node_get_int(value); case PROTOBUF_C_TYPE_STRING: { // if (maybe_clear && *pstr != NULL) { // const char *def = scanned_member->field->default_value; // if (*pstr != NULL && *pstr != def) // do_free(allocator, *pstr); *pstr = g_strdup(json_node_get_string(value)); case PROTOBUF_C_TYPE_BYTES: { ProtobufCBinaryData *bd = member; // def_bd = scanned_member->field->default_value; // (def_bd == NULL || bd->data != def_bd->data)) // do_free(allocator, bd->data); bd->data = g_base64_decode(json_node_get_string(value), &bd->len); case PROTOBUF_C_TYPE_MESSAGE: { ProtobufCMessage **pmessage = member; const ProtobufCMessageDescriptor *desc = field->descriptor; *pmessage = g_malloc0(desc->sizeof_message); (void) (glib_protobufc_allocator); protobuf_c_message_init(desc, *pmessage); switch(json_node_get_node_type(value)) { printf("value %s %" G_GINT64_FORMAT "\n", json_node_get_string(value), json_node_get_int(value)); return pblite_decode(*pmessage, json_node_get_array(value), FALSE); pblite_encode_field(const ProtobufCFieldDescriptor *field, gpointer value) case PROTOBUF_C_TYPE_INT32: case PROTOBUF_C_TYPE_UINT32: case PROTOBUF_C_TYPE_SFIXED32: case PROTOBUF_C_TYPE_FIXED32: case PROTOBUF_C_TYPE_FLOAT: case PROTOBUF_C_TYPE_ENUM: { uint32_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_SINT32: { int32_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_INT64: case PROTOBUF_C_TYPE_UINT64: case PROTOBUF_C_TYPE_SFIXED64: case PROTOBUF_C_TYPE_FIXED64: case PROTOBUF_C_TYPE_DOUBLE: { uint64_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_SINT64: { int64_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_BOOL: { protobuf_c_boolean * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_boolean(node, *member); case PROTOBUF_C_TYPE_STRING: { node = json_node_new(JSON_NODE_VALUE); json_node_set_string(node, *pstr); case PROTOBUF_C_TYPE_BYTES: { ProtobufCBinaryData *bd = value; b64_data = g_base64_encode(bd->data, bd->len); node = json_node_new(JSON_NODE_VALUE); json_node_set_string(node, b64_data); case PROTOBUF_C_TYPE_MESSAGE: { ProtobufCMessage **pmessage = value; node = json_node_new(JSON_NODE_ARRAY); json_node_take_array(node, pblite_encode(*pmessage)); pblite_decode_element(ProtobufCMessage *message, guint index, JsonNode *value) const ProtobufCFieldDescriptor *field; printf("pblite_decode_element field %d ", index); field = protobuf_c_message_descriptor_get_field(message->descriptor, index); gchar *json = json_pretty_encode(value, NULL); printf("skipped unknown %s\n", json); printf("is %s\n", field->name); if (JSON_NODE_HOLDS_NULL(value)) { printf("pblite_decode_element field %d skipped null\n", index); if (field->default_value != NULL) { *(const void **) STRUCT_MEMBER(void *, message, field->offset) = field->default_value; if (field->label == PROTOBUF_C_LABEL_REPEATED) { JsonArray *value_array = json_node_get_array(value); array_len = json_array_get_length(value_array); printf(" which is an array of length %d\n", array_len); siz = sizeof_elt_in_repeated_array(field->type); STRUCT_MEMBER(size_t, message, field->quantifier_offset) = array_len; STRUCT_MEMBER(void *, message, field->offset) = tmp = g_malloc0(siz * array_len); for (j = 0; j < array_len; j++) { if (JSON_NODE_HOLDS_NULL(json_array_get_element(value_array, j))) { printf("array %d contains null\n", j); success = pblite_decode_field(field, json_array_get_element(value_array, j), STRUCT_MEMBER(void *, message, field->offset) + (siz * j)); g_return_val_if_fail(success, FALSE); success = pblite_decode_field(field, value, STRUCT_MEMBER_P(message, field->offset)); g_return_val_if_fail(success, FALSE); if (field->label == PROTOBUF_C_LABEL_OPTIONAL && field->quantifier_offset) { STRUCT_MEMBER(protobuf_c_boolean, message, field->quantifier_offset) = TRUE; pblite_decode(ProtobufCMessage *message, JsonArray *pblite_array, gboolean ignore_first_item) const ProtobufCMessageDescriptor *descriptor = message->descriptor; guint offset = (ignore_first_item ? 1 : 0); gboolean last_element_is_object = FALSE; g_return_val_if_fail(descriptor, FALSE); len = json_array_get_length(pblite_array); printf("pblite_decode of %s with length %d\n", descriptor->name, len); if (JSON_NODE_HOLDS_OBJECT(json_array_get_element(pblite_array, len - 1))) { printf("ZOMG the last element is an object\n"); last_element_is_object = TRUE; for (i = offset; i < len; i++) { JsonNode *value = json_array_get_element(pblite_array, i); gboolean success = pblite_decode_element(message, i - offset + 1, value); g_return_val_if_fail(success, FALSE); //Continue on the array with the objects if (last_element_is_object) { JsonObject *last_object = json_array_get_object_element(pblite_array, len); GList *members = json_object_get_members(last_object); for (l = members; l != NULL; l = l->next) { const gchar *member_name = l->data; guint64 member = g_ascii_strtoull(member_name, NULL, 0); JsonNode *value = json_object_get_member(last_object, member_name); gboolean success = pblite_decode_element(message, member - offset, value); g_return_val_if_fail(success, FALSE); pblite_encode(ProtobufCMessage *message) //Maiku asked for more dinosaurs JsonArray *pblite = json_array_new(); JsonObject *cheats_object = json_object_new(); const ProtobufCMessageDescriptor *descriptor = message->descriptor; printf("pblite_encode of %s with length %d\n", descriptor->name, descriptor->n_fields); for (i = 0; i < descriptor->n_fields; i++) { const ProtobufCFieldDescriptor *field_descriptor = descriptor->fields + i; void *field = STRUCT_MEMBER_P(message, field_descriptor->offset); JsonNode *encoded_value = NULL; printf("pblite_encode_element field %d (%d) ", i, field_descriptor->id); printf("is %s\n", field_descriptor->name); if (field_descriptor->label == PROTOBUF_C_LABEL_REPEATED) { siz = sizeof_elt_in_repeated_array(field_descriptor->type); array_len = STRUCT_MEMBER(size_t, message, field_descriptor->quantifier_offset); printf(" which is an array of length %d\n", array_len); value_array = json_array_new(); for (j = 0; j < array_len; j++) { field = STRUCT_MEMBER(void *, message, field_descriptor->offset) + (siz * j); json_array_add_element(value_array, pblite_encode_field(field_descriptor, field)); encoded_value = json_node_new(JSON_NODE_ARRAY); json_node_take_array(encoded_value, value_array); if (field_descriptor->label == PROTOBUF_C_LABEL_OPTIONAL) { if (field_descriptor->type == PROTOBUF_C_TYPE_MESSAGE || field_descriptor->type == PROTOBUF_C_TYPE_STRING) const void *ptr = *(const void * const *) field; if (ptr == NULL || ptr == field_descriptor->default_value) encoded_value = json_node_new(JSON_NODE_NULL); const protobuf_c_boolean *val = STRUCT_MEMBER_P(message, field_descriptor->quantifier_offset); encoded_value = json_node_new(JSON_NODE_NULL); if (encoded_value == NULL) { encoded_value = pblite_encode_field(field_descriptor, field); if (json_array_get_length(pblite) + 1 == field_descriptor->id) { json_array_add_element(pblite, encoded_value); //need to insert at point [field_descriptor->id - 1] of the array, somehow //... so dont, just cheat! if (!JSON_NODE_HOLDS_NULL(encoded_value)) { gchar *obj_id = g_strdup_printf("%u", field_descriptor->id); json_object_set_member(cheats_object, obj_id, encoded_value); json_node_free(encoded_value); if (json_object_get_size(cheats_object)) { json_array_add_object_element(pblite, cheats_object); json_object_unref(cheats_object); static JsonObject *pblite_encode_for_json(ProtobufCMessage *message); pblite_encode_field_for_json(const ProtobufCFieldDescriptor *field, gpointer value) case PROTOBUF_C_TYPE_INT32: case PROTOBUF_C_TYPE_UINT32: case PROTOBUF_C_TYPE_SFIXED32: case PROTOBUF_C_TYPE_FIXED32: case PROTOBUF_C_TYPE_FLOAT: { uint32_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_ENUM: { uint32_t * member = value; const ProtobufCEnumDescriptor *enum_descriptor = field->descriptor; const ProtobufCEnumValue *enum_value = protobuf_c_enum_descriptor_get_value(enum_descriptor, *member); node = json_node_new(JSON_NODE_VALUE); if (enum_value == NULL) { gchar *unknown_text = g_strdup_printf("UNKNOWN ENUM VALUE %u", *member); json_node_set_string(node, unknown_text); json_node_set_string(node, enum_value->name); case PROTOBUF_C_TYPE_SINT32: { int32_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_INT64: case PROTOBUF_C_TYPE_UINT64: case PROTOBUF_C_TYPE_SFIXED64: case PROTOBUF_C_TYPE_FIXED64: case PROTOBUF_C_TYPE_DOUBLE: { uint64_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_SINT64: { int64_t * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_int(node, *member); case PROTOBUF_C_TYPE_BOOL: { protobuf_c_boolean * member = value; node = json_node_new(JSON_NODE_VALUE); json_node_set_boolean(node, *member); case PROTOBUF_C_TYPE_STRING: { node = json_node_new(JSON_NODE_VALUE); json_node_set_string(node, *pstr); case PROTOBUF_C_TYPE_BYTES: { ProtobufCBinaryData *bd = value; b64_data = g_base64_encode(bd->data, bd->len); node = json_node_new(JSON_NODE_VALUE); json_node_set_string(node, b64_data); case PROTOBUF_C_TYPE_MESSAGE: { ProtobufCMessage **pmessage = value; node = json_node_new(JSON_NODE_OBJECT); json_node_take_object(node, pblite_encode_for_json(*pmessage)); pblite_encode_for_json(ProtobufCMessage *message) JsonObject *pblite = json_object_new(); const ProtobufCMessageDescriptor *descriptor = message->descriptor; for (i = 0; i < descriptor->n_fields; i++) { const ProtobufCFieldDescriptor *field_descriptor = descriptor->fields + i; void *field = STRUCT_MEMBER_P(message, field_descriptor->offset); JsonNode *encoded_value = NULL; if (field_descriptor->label == PROTOBUF_C_LABEL_REPEATED) { siz = sizeof_elt_in_repeated_array(field_descriptor->type); array_len = STRUCT_MEMBER(size_t, message, field_descriptor->quantifier_offset); value_array = json_array_new(); for (j = 0; j < array_len; j++) { field = STRUCT_MEMBER(void *, message, field_descriptor->offset) + (siz * j); json_array_add_element(value_array, pblite_encode_field_for_json(field_descriptor, field)); encoded_value = json_node_new(JSON_NODE_ARRAY); json_node_take_array(encoded_value, value_array); if (field_descriptor->label == PROTOBUF_C_LABEL_OPTIONAL) { if (field_descriptor->type == PROTOBUF_C_TYPE_MESSAGE || field_descriptor->type == PROTOBUF_C_TYPE_STRING) const void *ptr = *(const void * const *) field; if (ptr == NULL || ptr == field_descriptor->default_value) encoded_value = json_node_new(JSON_NODE_NULL); const protobuf_c_boolean *val = STRUCT_MEMBER_P(message, field_descriptor->quantifier_offset); encoded_value = json_node_new(JSON_NODE_NULL); if (encoded_value == NULL) { encoded_value = pblite_encode_field_for_json(field_descriptor, field); json_object_set_member(pblite, field_descriptor->name, encoded_value); pblite_dump_json(ProtobufCMessage *message) JsonObject *pblite = pblite_encode_for_json(message); JsonNode *node = json_node_new(JSON_NODE_OBJECT); json_node_take_object(node, pblite); json = json_pretty_encode(node, NULL);