/** * inf_adopted_session_write_request_info: * @session: A #InfAdoptedSession. * @diff_vec: A reference state vector, or %NULL. * @request: The #InfAdoptedRequest whose info to write. * @xml: The XML node to write the data into. * @operation: An XML node representing the operation of the request, or * %NULL. * * This function writes common data from @request, such as the user that * issued the request and the state in which the request was made into @xml. * If @diff_vec is given, then the state is written as a diff to this vector, * see inf_adopted_state_vector_to_string_diff(). Deserializing this data * again (via inf_adopted_session_read_request_info()) requires the same * @diff_vec then. * * This function is most likely to be used by implementations of the * request_to_xml virtual function. */ void inf_adopted_session_write_request_info(InfAdoptedSession* session, InfAdoptedRequest* request, InfAdoptedStateVector* diff_vec, xmlNodePtr xml, xmlNodePtr operation) { InfAdoptedStateVector* vector; guint user_id; gchar* vec_str; vector = inf_adopted_request_get_vector(request); user_id = inf_adopted_request_get_user_id(request); inf_xml_util_set_attribute_uint(xml, "user", user_id); if(diff_vec == NULL) vec_str = inf_adopted_state_vector_to_string(vector); else vec_str = inf_adopted_state_vector_to_string_diff(vector, diff_vec); inf_xml_util_set_attribute(xml, "time", vec_str); g_free(vec_str); if(operation != NULL) xmlAddChild(xml, operation); }
/* Breadcasts a request N times - makes only sense for undo and redo requests, * so that's the only thing we offer API for. */ static void inf_adopted_session_broadcast_n_requests(InfAdoptedSession* session, InfAdoptedRequest* request, guint n) { InfAdoptedSessionPrivate* priv; InfAdoptedSessionClass* session_class; InfUserTable* user_table; guint user_id; InfUser* user; InfAdoptedSessionLocalUser* local; xmlNodePtr xml; priv = INF_ADOPTED_SESSION_PRIVATE(session); session_class = INF_ADOPTED_SESSION_GET_CLASS(session); g_assert(session_class->request_to_xml != NULL); user_table = inf_session_get_user_table(INF_SESSION(session)); user_id = inf_adopted_request_get_user_id(request); user = inf_user_table_lookup_user_by_id(user_table, user_id); g_assert(user != NULL); local = inf_adopted_session_lookup_local_user( session, INF_ADOPTED_USER(user) ); g_assert(local != NULL); xml = xmlNewNode(NULL, (const xmlChar*)"request"); session_class->request_to_xml( session, xml, request, local->last_send_vector, FALSE ); if(n > 1) inf_xml_util_set_attribute_uint(xml, "num", n); inf_session_send_to_subscriptions(INF_SESSION(session), xml); inf_adopted_state_vector_free(local->last_send_vector); local->last_send_vector = inf_adopted_state_vector_copy( inf_adopted_request_get_vector(request) ); /* Add this request to last send vector if it increases vector time * (-> affects buffer). */ if(inf_adopted_request_affects_buffer(request) == TRUE) inf_adopted_state_vector_add(local->last_send_vector, user_id, n); inf_adopted_session_stop_noop_timer(session, local); }
static InfCommunicationScope inf_adopted_session_process_xml_run(InfSession* session, InfXmlConnection* connection, const xmlNodePtr xml, GError** error) { InfAdoptedSessionPrivate* priv; InfAdoptedSessionClass* session_class; InfAdoptedRequest* request; InfAdoptedUser* user; gboolean has_num; guint num; GError* local_error; InfAdoptedRequest* copy_req; guint i; priv = INF_ADOPTED_SESSION_PRIVATE(session); if(strcmp((const char*)xml->name, "request") == 0) { session_class = INF_ADOPTED_SESSION_GET_CLASS(session); g_assert(session_class->xml_to_request != NULL); user = inf_adopted_session_user_from_request_xml( INF_ADOPTED_SESSION(session), xml, error ); if(user == NULL) return INF_COMMUNICATION_SCOPE_PTP; if(inf_user_get_status(INF_USER(user)) == INF_USER_UNAVAILABLE || inf_user_get_connection(INF_USER(user)) != connection) { g_set_error( error, inf_user_error_quark(), INF_USER_ERROR_NOT_JOINED, "%s", _("User did not join from this connection") ); return INF_COMMUNICATION_SCOPE_PTP; } local_error = NULL; has_num = inf_xml_util_get_attribute_uint(xml, "num", &num, &local_error); if(local_error != NULL) { g_propagate_error(error, local_error); return INF_COMMUNICATION_SCOPE_PTP; } request = session_class->xml_to_request( INF_ADOPTED_SESSION(session), xml, inf_adopted_user_get_vector(user), FALSE, error ); if(request == NULL) return INF_COMMUNICATION_SCOPE_PTP; inf_adopted_algorithm_receive_request(priv->algorithm, request); /* Apply the request more than once if num is given. This is mostly used * for multiple undos and redos, but is in general allowed for any * request. */ if(has_num) { for(i = 1; i < num; ++i) { /* TODO: This is a bit of a hack since requests are normally * immutable. It avoids an additional vector copy here though. */ copy_req = inf_adopted_request_copy(request); inf_adopted_state_vector_add( inf_adopted_request_get_vector(copy_req), inf_user_get_id(INF_USER(user)), i ); inf_adopted_algorithm_receive_request(priv->algorithm, copy_req); g_object_unref(copy_req); } } g_object_unref(request); /* Requests can always be forwarded since user is given. */ return INF_COMMUNICATION_SCOPE_GROUP; } return INF_SESSION_CLASS(parent_class)->process_xml_run( session, connection, xml, error ); }
/* TODO: Move into request log class? */ static gboolean inf_adopted_session_validate_request(InfAdoptedRequestLog* log, InfAdoptedRequest* request, GError** error) { InfAdoptedStateVector* vector; guint user_id; guint n; guint begin; guint end; vector = inf_adopted_request_get_vector(request); user_id = inf_adopted_request_get_user_id(request); n = inf_adopted_state_vector_get(vector, user_id); begin = inf_adopted_request_log_get_begin(log); end = inf_adopted_request_log_get_end(log); /* TODO: Actually, begin != end is only relevant for the first request * in request log. */ if(end != n && begin != end) { g_set_error( error, inf_adopted_session_error_quark, INF_ADOPTED_SESSION_ERROR_INVALID_REQUEST, _("Request has index '%u', but index '%u' was expected"), n, inf_adopted_request_log_get_end(log) ); return FALSE; } else { switch(inf_adopted_request_get_request_type(request)) { case INF_ADOPTED_REQUEST_DO: /* Nothing to check for */ return TRUE; case INF_ADOPTED_REQUEST_UNDO: if(inf_adopted_request_log_next_undo(log) == NULL) { g_set_error( error, inf_adopted_session_error_quark, INF_ADOPTED_SESSION_ERROR_INVALID_REQUEST, "%s", _("Undo received, but no previous request found") ); return FALSE; } else { return TRUE; } case INF_ADOPTED_REQUEST_REDO: if(inf_adopted_request_log_next_redo(log) == NULL) { g_set_error( error, inf_adopted_session_error_quark, INF_ADOPTED_SESSION_ERROR_INVALID_REQUEST, "%s", _("Redo received, but no previous request found") ); return FALSE; } else { return TRUE; } default: g_assert_not_reached(); return FALSE; } } }
static gboolean infinoted_plugin_transformation_protection_check_request_cb(InfAdoptedSession* session, InfAdoptedRequest* request, InfAdoptedUser* user, gpointer user_data) { InfinotedPluginTransformationProtectionSessionInfo* info; guint vdiff; InfXmlConnection* connection; gchar* request_str; gchar* current_str; gchar* remote_id; gchar* path; info = (InfinotedPluginTransformationProtectionSessionInfo*)user_data; vdiff = inf_adopted_state_vector_vdiff( inf_adopted_request_get_vector(request), inf_adopted_algorithm_get_current( inf_adopted_session_get_algorithm(session) ) ); if(vdiff > info->plugin->max_vdiff) { connection = inf_user_get_connection(INF_USER(user)); /* Local requests do not need to be transformed, so always have a * zero vdiff. */ g_assert(connection != NULL); /* Kill the connection */ infd_session_proxy_unsubscribe( INFD_SESSION_PROXY(info->proxy), connection ); /* Write a log message */ path = inf_browser_get_path( INF_BROWSER( infinoted_plugin_manager_get_directory(info->plugin->manager) ), &info->iter ); request_str = inf_adopted_state_vector_to_string( inf_adopted_request_get_vector(request) ); current_str = inf_adopted_state_vector_to_string( inf_adopted_algorithm_get_current( inf_adopted_session_get_algorithm(session) ) ); g_object_get(G_OBJECT(connection), "remote-id", &remote_id, NULL); infinoted_log_warning( infinoted_plugin_manager_get_log(info->plugin->manager), _("In document \"%s\": Attempt to transform request \"%s\" to current state \"%s\" " "(vdiff=%u) by user \"%s\" (id=%u, conn=%s). Maximum allowed is %u; the " "connection has been unsubscribed."), path, request_str, current_str, vdiff, inf_user_get_name(INF_USER(user)), inf_user_get_id(INF_USER(user)), remote_id, info->plugin->max_vdiff ); g_free(path); g_free(request_str); g_free(current_str); g_free(remote_id); /* Prevent the request from being transformed */ return TRUE; } return FALSE; }