const int getInt (JsonReader * reader, const gchar * member_name) { json_reader_read_member (reader, member_name); int value = json_reader_get_int_value (reader); json_reader_end_member (reader); return value; }
static guint64 reader_get_item_count (JsonReader *reader) { gint64 count; json_reader_read_member (reader, "count"); count = json_reader_get_int_value (reader); json_reader_end_member (reader); return (guint64) count; }
static int websocket_callback(struct libwebsocket_context *context,struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason, void *user,void *in, size_t len) { //printf("Switch: %i\n",reason); switch (reason) { case LWS_CALLBACK_CLIENT_WRITEABLE: { //Connection has been established. //printf("Connection established\n"); break; } case LWS_CALLBACK_CLOSED: { //Connection is closed, we need to remove all related sinks sinkManager->disconnectAll(wsi); /*g_io_ GIOChannel *chan = g_io_channel_unix_new((int)(long)user); g_io_add_watch(chan,G_IO_IN,(GIOFunc)gioPollingFunc,0); g_io_add_watch(chan,G_IO_PRI,(GIOFunc)gioPollingFunc,0); pollfds[count_pollfds].fd = (int)(long)user; pollfds[count_pollfds].events = (int)len; // pollfds[count_pollfds++].revents = 0;*/ break; } case LWS_CALLBACK_CLIENT_RECEIVE: { //printf("Client writable\n"); break; } case LWS_CALLBACK_SERVER_WRITEABLE: { //printf("Server writable\n"); break; } case LWS_CALLBACK_RECEIVE: { //printf("Data Received: %s\n",(char*)in); //The lack of a break; here is intentional. } case LWS_CALLBACK_HTTP: { //TODO: Verify that ALL requests get sent via LWS_CALLBACK_HTTP, so we can use that instead of LWS_CALLBACK_RECIEVE //TODO: Do we want exceptions, or just to return an invalid json reply? Probably an invalid json reply. DebugOut() << __SMALLFILE__ << ":" << __LINE__ << " Requested: " << (char*)in << "\n"; GError* error = nullptr; JsonParser* parser = json_parser_new(); if (!json_parser_load_from_data(parser,(char*)in,len,&error)) { DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error loading JSON\n"; return 0; } JsonNode* node = json_parser_get_root(parser); if(node == nullptr) { DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error getting root node of json\n"; //throw std::runtime_error("Unable to get JSON root object"); return 0; } JsonReader* reader = json_reader_new(node); if(reader == nullptr) { DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "json_reader is null!\n"; //throw std::runtime_error("Unable to create JSON reader"); return 0; } string type; json_reader_read_member(reader,"type"); type = json_reader_get_string_value(reader); json_reader_end_member(reader); string name; json_reader_read_member(reader,"name"); name = json_reader_get_string_value(reader); json_reader_end_member(reader); string id; json_reader_read_member(reader,"transactionid"); if (strcmp("gchararray",g_type_name(json_node_get_value_type(json_reader_get_value(reader)))) == 0) { //Type is a string id = json_reader_get_string_value(reader); } else { //Type is an integer stringstream strstr; strstr << json_reader_get_int_value(reader); id = strstr.str(); } json_reader_end_member(reader); if (type == "method" && name == "getRanged") { json_reader_read_member(reader,"data"); if (json_reader_is_object(reader)) { double timeBegin; double timeEnd; double sequenceBegin; double sequenceEnd; string property; if (json_reader_read_member(reader,"timeBegin")) { timeBegin = boost::lexical_cast<double,std::string>(json_reader_get_string_value(reader)); json_reader_end_member(reader); } if (json_reader_read_member(reader,"timeEnd")) { timeEnd = boost::lexical_cast<double,std::string>(json_reader_get_string_value(reader)); json_reader_end_member(reader); } if (json_reader_read_member(reader,"sequenceBegin")) { sequenceBegin = boost::lexical_cast<double,std::string>(json_reader_get_string_value(reader)); json_reader_end_member(reader); } if (json_reader_read_member(reader,"sequenceEnd")) { sequenceEnd = boost::lexical_cast<double,std::string>(json_reader_get_string_value(reader)); json_reader_end_member(reader); } if (json_reader_read_member(reader,"property")) { property = json_reader_get_string_value(reader); json_reader_end_member(reader); } if ((timeBegin < 0 && timeEnd > 0) || (timeBegin > 0 && timeEnd < 0)) { //Invalid time begin/end pair } if ((sequenceBegin < 0 && sequenceEnd > 0) || (sequenceBegin > 0 && sequenceEnd < 0)) { //Invalid sequence begin/end pair } sinkManager->addSingleShotRangedSink(wsi,property,timeBegin,timeEnd,sequenceBegin,sequenceEnd,id); } json_reader_end_member(reader); } else { vector<string> data; list<string> key; list<string> value; json_reader_read_member(reader,"data"); if (json_reader_is_array(reader)) { for(int i=0; i < json_reader_count_elements(reader); i++) { json_reader_read_element(reader,i); if (json_reader_is_value(reader)) { //Raw string value string path = json_reader_get_string_value(reader); data.push_back(path); } else { //Not a raw string value, then it's "property/value" kvp, for "set" requests json_reader_read_member(reader,"property"); string keystr = json_reader_get_string_value(reader); key.push_back(keystr); json_reader_end_member(reader); json_reader_read_member(reader,"value"); string valuestr = json_reader_get_string_value(reader); value.push_back(valuestr); json_reader_end_member(reader); } json_reader_end_element(reader); } } else { string path = json_reader_get_string_value(reader); if (path != "") { data.push_back(path); } } json_reader_end_member(reader); if (type == "method") { if (name == "get") { if (data.size() > 0) { //GetProperty is going to be a singleshot sink. //string arg = arguments.front(); sinkManager->addSingleShotSink(wsi,data.front(),id); } else { DebugOut() << __SMALLFILE__ << ":" << __LINE__ << " \"get\" method called with no data! Transaction ID:" << id << "\n"; } } else if (name == "set") { if (data.size() > 0) { //Should not happen } else if (value.size() > 0) { if (key.size() != value.size()) { DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "\"set\" method called with an invalid key value pair count\n"; } else { list<string>::iterator d = value.begin(); for (list<string>::iterator i=key.begin();i!=key.end();i++) { DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "websocketsinkmanager setting" << (*i) << "to" << (*d) << "\n"; //(*i); sinkManager->setValue((*i),(*d)); //(*d); d++; } } } } else if (name == "subscribe") { //Websocket wants to subscribe to an event, data.front(); for (auto i=data.begin();i!=data.end();i++) { sinkManager->addSink(wsi,(*i),id); } } else if (name == "unsubscribe") { //Websocket wants to unsubscribe to an event, data.front(); for (auto i=data.begin();i!=data.end();i++) { sinkManager->removeSink(wsi,(*i),id); } } else if (name == "getSupportedEventTypes") { //If data.front() dosen't contain a property name, return a list of properties supported. //if it does, then return the event types that particular property supports. string typessupported = ""; if (data.size() == 0) { //Send what properties we support typessupported = "\"running_status_speedometer\",\"running_status_engine_speed\",\"running_status_steering_wheel_angle\",\"running_status_transmission_gear_status\""; PropertyList foo = sinkManager->getSupportedProperties(); PropertyList::const_iterator i=foo.cbegin(); while (i != foo.cend()) { typessupported.append(",\"").append((*i)).append("\""); i++; } } else { //Send what events a particular property supports if (data.front()== "running_status_speedometer") { typessupported = "\"get\",\"subscribe\",\"unsubscribe\",\"getSupportedEventTypes\""; } else if (data.front()== "running_status_engine_speed") { typessupported = "\"get\",\"subscribe\",\"unsubscribe\",\"getSupportedEventTypes\""; } else if (data.front() == "running_status_steering_wheel_angle") { typessupported = "\"get\",\"subscribe\",\"unsubscribe\",\"getSupportedEventTypes\""; } else if (data.front() == "running_status_transmission_gear_status") { typessupported = "\"get\",\"subscribe\",\"unsubscribe\",\"getSupportedEventTypes\""; } else { PropertyList foo = sinkManager->getSupportedProperties(); if (ListPlusPlus<VehicleProperty::Property>(&foo).contains(data.front())) { //sinkManager->addSingleShotSink(wsi,data.front(),id); typessupported = "\"get\",\"subscribe\",\"unsubscribe\",\"getSupportedEventTypes\""; } } } stringstream s; string s2; s << "{\"type\":\"methodReply\",\"name\":\"getSupportedEventTypes\",\"data\":[" << typessupported << "],\"transactionid\":\"" << id << "\"}"; string replystr = s.str(); DebugOut() << __SMALLFILE__ << ":" << __LINE__ << " JSON Reply: " << replystr << "\n"; //printf("Reply: %s\n",replystr.c_str()); char *new_response = new char[LWS_SEND_BUFFER_PRE_PADDING + strlen(replystr.c_str()) + LWS_SEND_BUFFER_POST_PADDING]; new_response+=LWS_SEND_BUFFER_PRE_PADDING; strcpy(new_response,replystr.c_str()); libwebsocket_write(wsi, (unsigned char*)new_response, strlen(new_response), LWS_WRITE_TEXT); delete (char*)(new_response-LWS_SEND_BUFFER_PRE_PADDING); } else { DebugOut(0)<<"Unknown method called."<<endl; } } } ///TODO: this will probably explode: //mlc: I agree with Kevron here, it does explode. //if(error) g_error_free(error); g_object_unref(reader); g_object_unref(parser); break; } case LWS_CALLBACK_ADD_POLL_FD: { //printf("Adding poll %i\n",sinkManager); //DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Adding poll" << (int)sinkManager << "\n"; if (sinkManager != 0) { sinkManager->addPoll((int)(long)user); } break; } case LWS_CALLBACK_DEL_POLL_FD: { sinkManager->removePoll((int)(long)user); break; } case LWS_CALLBACK_SET_MODE_POLL_FD: { //Set the poll mode break; } case LWS_CALLBACK_CLEAR_MODE_POLL_FD: { //Don't handle this yet. break; } default: { //printf("Unhandled callback: %i\n",reason); DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unhandled callback:" << reason << "\n"; break; } } return 0; }
/* Save key/values on the table in the stack if the value is an * object or an array, it calls recursively the function again. * * @param L, pointer to the L with nil on top of it; * @param reader, pointed to the first element of main object; * * returns: the table in the stack with all json values */ static void build_table_from_json_reader (lua_State *L, JsonReader *reader) { const GError *err = json_reader_get_error (reader); if (err != NULL) { GRL_WARNING ("Error when building json: %s", err->message); return; } if (lua_isnil (L, -1)) { /* In the first execution of this recursive call, the main json object * does not have a member name. The nil is in the top of the stack and * it shall be converted to the table with json content */ lua_pop (L, 1); } else if (lua_istable (L, -1)) { const gchar *member_name = json_reader_get_member_name (reader); if (member_name) lua_pushstring (L, member_name); } else if (!lua_isnumber (L, -1)) { GRL_DEBUG ("getting value to either table or array"); return; } if (json_reader_is_object (reader)) { guint index_member = 0; guint num_members = json_reader_count_members (reader); lua_createtable (L, num_members, 0); for (index_member = 0; index_member < num_members; index_member++) { json_reader_read_element (reader, index_member); build_table_from_json_reader (L, reader); json_reader_end_element (reader); } } else if (json_reader_is_array (reader)) { guint index_element = 0; guint num_elements = json_reader_count_elements (reader); lua_createtable (L, num_elements, 0); for (index_element = 0; index_element < num_elements; index_element++) { json_reader_read_element (reader, index_element); lua_pushinteger (L, index_element + 1); build_table_from_json_reader (L, reader); json_reader_end_element (reader); } } else if (json_reader_is_value (reader)) { if (json_reader_get_null_value (reader)) { lua_pushnil (L); } else { /* value of the element */ JsonNode *value = json_reader_get_value (reader); switch (json_node_get_value_type (value)) { case G_TYPE_STRING: lua_pushstring (L, json_reader_get_string_value (reader)); break; case G_TYPE_INT64: lua_pushinteger (L, json_reader_get_int_value (reader)); break; case G_TYPE_DOUBLE: lua_pushnumber (L, json_reader_get_double_value (reader)); break; case G_TYPE_BOOLEAN: lua_pushnumber (L, json_reader_get_boolean_value (reader)); break; default: GRL_DEBUG ("'%d' (json-node-type) is not being handled", (gint) json_node_get_value_type (value)); lua_pushnil (L); } } } if (lua_gettop (L) > 3) { /* save this key/value on previous table */ lua_settable (L, -3); } }
static gboolean dt_noiseprofile_verify(JsonParser *parser) { JsonReader *reader = NULL; gboolean valid = TRUE; dt_print(DT_DEBUG_CONTROL, "[noiseprofile] verifying noiseprofile file\n"); JsonNode *root = json_parser_get_root(parser); if(!root) _ERROR("can't get the root node"); reader = json_reader_new(root); if(!json_reader_read_member(reader, "version")) _ERROR("can't find file version."); // check the file version const int version = json_reader_get_int_value(reader); json_reader_end_member(reader); if(version != DT_NOISE_PROFILE_VERSION) _ERROR("file version is not what this code understands"); if(!json_reader_read_member(reader, "noiseprofiles")) _ERROR("can't find `noiseprofiles' entry."); if(!json_reader_is_array(reader)) _ERROR("`noiseprofiles' is supposed to be an array"); // go through all makers const int n_makers = json_reader_count_elements(reader); dt_print(DT_DEBUG_CONTROL, "[noiseprofile] found %d makers\n", n_makers); for(int i = 0; i < n_makers; i++) { if(!json_reader_read_element(reader, i)) _ERROR("can't access maker at position %d / %d", i+1, n_makers); if(!json_reader_read_member(reader, "maker")) _ERROR("missing `maker`"); dt_print(DT_DEBUG_CONTROL, "[noiseprofile] found maker `%s'\n", json_reader_get_string_value(reader)); // go through all models and check those json_reader_end_member(reader); if(!json_reader_read_member(reader, "models")) _ERROR("missing `models`"); const int n_models = json_reader_count_elements(reader); dt_print(DT_DEBUG_CONTROL, "[noiseprofile] found %d models\n", n_models); for(int j = 0; j < n_models; j++) { if(!json_reader_read_element(reader, j)) _ERROR("can't access model at position %d / %d", j+1, n_models); if(!json_reader_read_member(reader, "model")) _ERROR("missing `model`"); dt_print(DT_DEBUG_CONTROL, "[noiseprofile] found %s\n", json_reader_get_string_value(reader)); json_reader_end_member(reader); if(!json_reader_read_member(reader, "profiles")) _ERROR("missing `profiles`"); const int n_profiles = json_reader_count_elements(reader); dt_print(DT_DEBUG_CONTROL, "[noiseprofile] found %d profiles\n", n_profiles); for(int k = 0; k < n_profiles; k++) { if(!json_reader_read_element(reader, k)) _ERROR("can't access profile at position %d / %d", k+1, n_profiles); gchar** member_names = json_reader_list_members(reader); // name if(!is_member(member_names, "name")) { g_strfreev(member_names); _ERROR("missing `name`"); } // iso if(!is_member(member_names, "iso")) { g_strfreev(member_names); _ERROR("missing `iso`"); } // a if(!is_member(member_names, "a")) { g_strfreev(member_names); _ERROR("missing `a`"); } json_reader_read_member(reader, "a"); if(json_reader_count_elements(reader) != 3) { g_strfreev(member_names); _ERROR("`a` with size != 3"); } json_reader_end_member(reader); // b if(!is_member(member_names, "b")) { g_strfreev(member_names); _ERROR("missing `b`"); } json_reader_read_member(reader, "b"); if(json_reader_count_elements(reader) != 3) { g_strfreev(member_names); _ERROR("`b` with size != 3"); } json_reader_end_member(reader); json_reader_end_element(reader); g_strfreev(member_names); } // profiles json_reader_end_member(reader); json_reader_end_element(reader); } // models json_reader_end_member(reader); json_reader_end_element(reader); } // makers json_reader_end_member(reader); dt_print(DT_DEBUG_CONTROL, "[noiseprofile] verifying noiseprofile completed\n"); end: if(reader) g_object_unref(reader); return valid; }
static gboolean handler (GThreadedSocketService *service, GSocketConnection *connection, GSocketListener *listener, gpointer user_data) { GLibJsonRpcServerPrivate *jsonrpc_server = (GLibJsonRpcServerPrivate*)user_data; GError *error = NULL; // Check if it is a local connection. - This doesn't work! GSocketAddress *sockaddr = g_socket_connection_get_remote_address(connection, &error); GInetAddress *addr = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(sockaddr)); if (!jsonrpc_server->allow_non_loopback_connections) { #if 0 // Why doesn't this work? if (!g_inet_address_get_is_loopback(addr)) return TRUE; // just fail #endif gchar *addr_string = g_inet_address_to_string(addr); gboolean is_local = g_strstr_len(addr_string, -1, "127.0.0.1") != NULL; g_free(addr_string); if (!is_local) return TRUE; // silently fail } GOutputStream *out; GInputStream *in; char buffer[1024]; gssize size; out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); in = g_io_stream_get_input_stream (G_IO_STREAM (connection)); // Read the http header gboolean skip_header = TRUE; JsonParser *parser = json_parser_new(); GString *json_string = g_string_new(""); while (0 < (size = g_input_stream_read (in, buffer, sizeof buffer, NULL, NULL))) { int header_size = 0; if (skip_header) { gchar *head_end = g_strstr_len(buffer, size, "\r\n\r\n"); if (head_end > 0) header_size = head_end - buffer; else continue; } g_string_append_len(json_string, buffer+header_size, size-header_size); if (json_parser_load_from_data(parser, json_string->str, -1, &error)) break; else g_error_free(error); } // TBD: raise error if there was a syntax error g_string_free(json_string, TRUE); // Get params object without the reader JsonNode *root = json_parser_get_root(parser); JsonObject *root_object = json_node_get_object(root); JsonNode* params = json_object_get_member(root_object, "params"); // Use reader for method and id JsonReader *reader = json_reader_new(json_parser_get_root(parser)); json_reader_read_member (reader, "method"); const gchar *method = json_reader_get_string_value (reader); json_reader_end_member (reader); json_reader_read_member (reader, "id"); gint64 id = json_reader_get_int_value(reader); json_reader_end_member (reader); // Build the response which is either a response object or an error object JsonNode *response = NULL; /* Call the callback */ command_hash_value_t *command_val = NULL; if (method) command_val = g_hash_table_lookup(jsonrpc_server->command_hash, method); if (!command_val) response = create_fault_msg_response(-2, "No such method!",id); else if (command_val->async_callback) { if (jsonrpc_server->async_busy) response = create_fault_msg_response(-2, "Busy!",id); else { // With the embedding of the mutex in the query we should // be able to handle more than one connection, so there // is no need to protect against a busy state. // jsonrpc_server->async_busy = TRUE; GLibJsonRpcAsyncQueryPrivate *query = glib_jsonrpc_server_query_new((GLibJsonRpcServer*)jsonrpc_server); // Create a secondary main loop (*command_val->async_callback)((GLibJsonRpcServer*)jsonrpc_server, (GLibJsonRpcAsyncQuery*)query, method, params, command_val->user_data); // Lock on a mutex g_mutex_lock(&query->mutex); g_cond_wait(&query->cond, &query->mutex); g_mutex_unlock(&query->mutex); if (query->error_num != 0) response = create_fault_value_response(query->error_num,query->reply,id); else response = create_response(query->reply, id); jsonrpc_server->async_busy = FALSE; // Erase the query glib_jsonrpc_async_query_free(query); } } else { JsonNode *reply; int ret = (*command_val->callback)((GLibJsonRpcServer*)jsonrpc_server, method, params, &reply, command_val->user_data); if (ret == 0) response = create_response(reply,id); else // For faults expect a string response containing the error response = create_fault_value_response(ret, reply,id); if (reply) json_node_free(reply); } if (response) { GString *response_string = g_string_new(""); // Serialize response into content_string JsonGenerator *gen = json_generator_new (); gsize len; json_generator_set_root (gen, response); json_node_free(response); gchar *content_string = json_generator_to_data(gen, &len); g_object_unref (gen); g_string_append_printf(response_string, "HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "Content-Type: text/xml\r\n" "Date: Fri, 1 Jan 2000 00:00:00 GMT\r\n" "Server: GlibJsonRPC server\r\n" "\r\n" "%s", (int)len, content_string ); g_free(content_string); g_output_stream_write (out, response_string->str, response_string->len, NULL,NULL); g_string_free(response_string, TRUE); } g_object_unref(parser); return TRUE; }
static void restore_state(session_callback_type type, gpointer data, gpointer user_data) { JsonParser* jp = NULL; JsonReader* jr = NULL; const gchar* sqs; saved_state* s = NULL; GError* err = NULL; /* Is it the callback we're interested in? */ if (type != SPOP_SESSION_LOGGED_IN) return; /* First disable the callback so it's not called again */ session_remove_callback(restore_state, NULL); g_debug("savestate: reading saved state..."); s = g_new0(saved_state, 1); /* Read and parse state file */ jp = json_parser_new(); if (!json_parser_load_from_file(jp, g_state_file_path, &err)) { g_warning("savestate: error while reading state file: %s", err->message); goto restorestate_error; } jr = json_reader_new(json_parser_get_root(jp)); /* Read basic state */ if (!json_reader_read_member(jr, "status")) goto restorestate_jr_error; sqs = json_reader_get_string_value(jr); json_reader_end_member(jr); if (strcmp(sqs, "stopped")) s->qs = STOPPED; else if (strcmp(sqs, "playing")) s->qs = PLAYING; else if (strcmp(sqs, "paused")) s->qs = PAUSED; else { g_warning("savestate: bad value for queue status: %s", sqs); goto restorestate_error; } if (!json_reader_read_member(jr, "repeat")) goto restorestate_jr_error; s->repeat = json_reader_get_boolean_value(jr); json_reader_end_member(jr); if (!json_reader_read_member(jr, "shuffle")) goto restorestate_jr_error; s->shuffle = json_reader_get_boolean_value(jr); json_reader_end_member(jr); if (!json_reader_read_member(jr, "current_track")) goto restorestate_jr_error; s->cur_track = json_reader_get_int_value(jr); json_reader_end_member(jr); /* Now read tracks URIs */ if (!json_reader_read_member(jr, "tracks")) goto restorestate_jr_error; if (!json_reader_is_array(jr)) { g_warning("savestate: error while parsing JSON: tracks is not an array"); goto restorestate_error; } gint tracks = json_reader_count_elements(jr); if (s->cur_track >= tracks) { g_warning("savestate: incoherent state file: cur_track >= tracks"); goto restorestate_error; } s->tracks = g_array_sized_new(FALSE, FALSE, sizeof(sp_track*), tracks); if (!s->tracks) g_error("Can't allocate array of %d tracks.", tracks); size_t i; gboolean can_restore_now = TRUE; for (i=0; i < tracks; i++) { json_reader_read_element(jr, i); const gchar* uri = json_reader_get_string_value(jr); json_reader_end_element(jr); sp_link* lnk = sp_link_create_from_string(uri); sp_linktype lt = sp_link_type(lnk); if (lt != SP_LINKTYPE_TRACK) { g_warning("savestate: invalid link type for track %zu: %d", i, lt); sp_link_release(lnk); goto restorestate_error; } sp_track* tr = sp_link_as_track(lnk); sp_track_add_ref(tr); sp_link_release(lnk); g_array_append_val(s->tracks, tr); if (!sp_track_is_loaded(tr)) can_restore_now = FALSE; } /* If possible, restore now, else wait for all tracks to be loaded */ if (can_restore_now) really_restore_state(s); else { g_timeout_add(100, (GSourceFunc) really_restore_state, s); g_debug("savestate: waiting for all tracks to be loaded before restoring saved state..."); } /* Add a notification callback */ if (!interface_notify_add_callback(savestate_notification_callback, NULL)) g_error("Could not add savestate callback."); goto restorestate_clean; restorestate_jr_error: err = (GError*) json_reader_get_error(jr); g_warning("savestate: error while parsing JSON: %s", err->message); restorestate_error: if (s) { if (s->tracks) g_array_free(s->tracks, TRUE); g_free(s); } restorestate_clean: if (jp) g_object_unref(jp); if (jr) g_object_unref(jr); }