pidgin/pidgin

Update the date in COPYRIGHT as it was a few years behind
release-2.x.y
2 months ago, Gary Kramlich
21a56db5f998
Update the date in COPYRIGHT as it was a few years behind

Testing Done:
None

Reviewed at https://reviews.imfreedom.org/r/3007/
/*
* purple - Jabber Protocol Plugin
*
* 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 <glib.h>
#include "namespaces.h"
#include "xmlnode.h"
#include "jabber.h"
#include "debug.h"
#include "notify.h"
#include "stream_management.h"
#define MAX_QUEUE_LENGTH 10000
GHashTable *jabber_sm_accounts;
static void
jabber_sm_accounts_queue_free(gpointer q)
{
g_queue_free_full(q, (GDestroyNotify)xmlnode_free);
}
/* Returns a queue for a JabberStream's account (based on JID),
creates it if there's none. */
static GQueue *
jabber_sm_accounts_queue_get(JabberStream *js)
{
GQueue *queue;
gchar *jid = jabber_id_get_bare_jid(js->user);
if (g_hash_table_contains(jabber_sm_accounts, jid) == TRUE) {
queue = g_hash_table_lookup(jabber_sm_accounts, jid);
g_free(jid);
} else {
queue = g_queue_new();
g_hash_table_insert(jabber_sm_accounts, jid, queue);
}
return queue;
}
void
jabber_sm_init(void)
{
jabber_sm_accounts = g_hash_table_new_full(g_str_hash, g_str_equal, free,
jabber_sm_accounts_queue_free);
}
void
jabber_sm_uninit(void)
{
g_hash_table_destroy(jabber_sm_accounts);
}
/* Processes incoming NS_STREAM_MANAGEMENT packets. */
void
jabber_sm_process_packet(JabberStream *js, xmlnode *packet) {
gchar *jid;
const char *name = packet->name;
if (purple_strequal(name, "enabled")) {
purple_debug_info("XEP-0198", "Stream management is enabled\n");
js->sm_inbound_count = 0;
js->sm_state = SM_ENABLED;
} else if (purple_strequal(name, "failed")) {
purple_debug_error("XEP-0198", "Failed to enable stream management\n");
js->sm_state = SM_DISABLED;
jid = jabber_id_get_bare_jid(js->user);
g_hash_table_remove(jabber_sm_accounts, jid);
g_free(jid);
} else if (purple_strequal(name, "r")) {
jabber_sm_ack_send(js);
} else if (purple_strequal(name, "a")) {
jabber_sm_ack_read(js, packet);
} else {
purple_debug_error("XEP-0198", "Unknown packet: %s\n", name);
}
}
/* Sends an acknowledgement. */
void
jabber_sm_ack_send(JabberStream *js)
{
xmlnode *ack;
char *ack_h;
if (js->sm_state != SM_ENABLED) {
return;
}
ack = xmlnode_new("a");
ack_h = g_strdup_printf("%u", js->sm_inbound_count);
xmlnode_set_namespace(ack, NS_STREAM_MANAGEMENT);
xmlnode_set_attrib(ack, "h", ack_h);
jabber_send(js, ack);
xmlnode_free(ack);
g_free(ack_h);
}
/* Reads acknowledgements, removes queued stanzas. */
void
jabber_sm_ack_read(JabberStream *js, xmlnode *packet)
{
guint32 i;
guint32 h;
GQueue *queue;
xmlnode *stanza;
const char *ack_h = xmlnode_get_attrib(packet, "h");
if (ack_h == NULL) {
purple_debug_error("XEP-0198",
"The 'h' attribute is not defined for an answer.");
return;
}
h = strtoul(ack_h, NULL, 10);
/* Remove stanzas from the queue */
queue = jabber_sm_accounts_queue_get(js);
for (i = js->sm_outbound_confirmed; i < h; i++) {
stanza = g_queue_pop_head(queue);
if (stanza == NULL) {
purple_debug_error("XEP-0198", "The queue is empty\n");
break;
}
xmlnode_free(stanza);
}
js->sm_outbound_confirmed = h;
purple_debug_info("XEP-0198",
"Acknowledged %u out of %u outbound stanzas\n",
js->sm_outbound_confirmed, js->sm_outbound_count);
}
/* Asks a server to enable stream management, resends queued
stanzas. */
void
jabber_sm_enable(JabberStream *js)
{
xmlnode *enable;
xmlnode *stanza;
GQueue *queue;
guint queue_len;
guint i;
js->server_caps |= JABBER_CAP_STREAM_MANAGEMENT;
purple_debug_info("XEP-0198", "Enabling stream management\n");
enable = xmlnode_new("enable");
xmlnode_set_namespace(enable, NS_STREAM_MANAGEMENT);
jabber_send(js, enable);
xmlnode_free(enable);
js->sm_outbound_count = 0;
js->sm_outbound_confirmed = 0;
js->sm_state = SM_REQUESTED;
/* Resend unacknowledged stanzas from the queue. */
queue = jabber_sm_accounts_queue_get(js);
queue_len = g_queue_get_length(queue);
if (queue_len > 0) {
purple_debug_info("XEP-0198", "Resending %u stanzas\n", queue_len);
}
for (i = 0; i < queue_len; i++) {
stanza = g_queue_pop_head(queue);
jabber_send(js, stanza);
xmlnode_free(stanza);
}
}
/* Tracks outbound stanzas, stores those into a queue, requests
acknowledgements. */
void
jabber_sm_outbound(JabberStream *js, xmlnode *packet)
{
if (jabber_is_stanza(packet)
&& (js->sm_state == SM_REQUESTED || js->sm_state == SM_ENABLED)) {
/* Counting stanzas even if there's no confirmation that SM is
enabled yet, so that we won't miss any. */
/* Add this stanza to the queue, unless the queue is full. */
xmlnode *stanza;
xmlnode *req;
GQueue *queue = jabber_sm_accounts_queue_get(js);
if (g_queue_get_length(queue) < MAX_QUEUE_LENGTH) {
stanza = xmlnode_copy(packet);
g_queue_push_tail(queue, stanza);
if (g_queue_get_length(queue) == MAX_QUEUE_LENGTH) {
gchar *jid;
gchar *queue_is_full_message;
jid = jabber_id_get_bare_jid(js->user);
queue_is_full_message =
g_strdup_printf(
_("The queue for %s has reached its maximum length of %u."),
jid, MAX_QUEUE_LENGTH);
purple_debug_warning("XEP-0198",
"Stanza queue for %s is full (%u stanzas).\n",
jid, MAX_QUEUE_LENGTH);
g_free(jid);
purple_notify_formatted(js->gc, _("XMPP stream management"),
_("Stanza queue is full"),
_("No further messages will be queued"),
queue_is_full_message,
NULL, NULL);
g_free(queue_is_full_message);
}
}
/* Count the stanza */
js->sm_outbound_count++;
/* Requesting acknowledgements with either SM_REQUESTED or
SM_ENABLED state as well, so that it would be harder to lose
stanzas. */
req = xmlnode_new("r");
xmlnode_set_namespace(req, NS_STREAM_MANAGEMENT);
jabber_send(js, req);
xmlnode_free(req);
}
}
/* Counts inbound stanzas. */
void
jabber_sm_inbound(JabberStream *js, xmlnode *packet)
{
/* Count stanzas for XEP-0198, excluding stream management
packets. */
if (jabber_is_stanza(packet)) {
js->sm_inbound_count++;
}
}