pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Update the ChangeLog and configure.ac for release
release-2.x.y
v2.14.2
2021-04-01, Gary Kramlich
2eb25613d054
Update the ChangeLog and configure.ac for release
Testing Done:
Ran `make dist-check` locally.
Reviewed at https://reviews.imfreedom.org/r/590/
/*
* purple - Jabber XML parser stuff
*
* 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
<libxml/parser.h>
#include
"connection.h"
#include
"debug.h"
#include
"jabber.h"
#include
"parser.h"
#include
"util.h"
#include
"xmlnode.h"
static
void
jabber_parser_element_start_libxml
(
void
*
user_data
,
const
xmlChar
*
element_name
,
const
xmlChar
*
prefix
,
const
xmlChar
*
namespace
,
int
nb_namespaces
,
const
xmlChar
**
namespaces
,
int
nb_attributes
,
int
nb_defaulted
,
const
xmlChar
**
attributes
)
{
JabberStream
*
js
=
user_data
;
xmlnode
*
node
;
int
i
,
j
;
if
(
!
element_name
)
{
return
;
}
else
if
(
js
->
stream_id
==
NULL
)
{
/* Sanity checking! */
if
(
0
!=
xmlStrcmp
(
element_name
,
(
xmlChar
*
)
"stream"
)
||
0
!=
xmlStrcmp
(
namespace
,
(
xmlChar
*
)
NS_XMPP_STREAMS
))
{
/* We were expecting a <stream:stream/> opening stanza, but
* didn't get it. Bad!
*/
purple_debug_error
(
"jabber"
,
"Expecting stream header, got %s with "
"xmlns %s
\n
"
,
element_name
,
namespace
);
purple_connection_error_reason
(
js
->
gc
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"XMPP stream header missing"
));
return
;
}
js
->
protocol_version
.
major
=
0
;
js
->
protocol_version
.
minor
=
9
;
for
(
i
=
0
;
i
<
nb_attributes
*
5
;
i
+=
5
)
{
int
attrib_len
=
attributes
[
i
+
4
]
-
attributes
[
i
+
3
];
char
*
attrib
=
g_strndup
((
gchar
*
)
attributes
[
i
+
3
],
attrib_len
);
if
(
!
xmlStrcmp
(
attributes
[
i
],
(
xmlChar
*
)
"version"
))
{
const
char
*
dot
=
strchr
(
attrib
,
'.'
);
js
->
protocol_version
.
major
=
atoi
(
attrib
);
js
->
protocol_version
.
minor
=
dot
?
atoi
(
dot
+
1
)
:
0
;
if
(
js
->
protocol_version
.
major
>
1
)
{
/* TODO: Send <unsupported-version/> error */
purple_connection_error_reason
(
js
->
gc
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"XMPP Version Mismatch"
));
g_free
(
attrib
);
return
;
}
if
(
js
->
protocol_version
.
major
==
0
&&
js
->
protocol_version
.
minor
!=
9
)
{
purple_debug_warning
(
"jabber"
,
"Treating version %s as 0.9 for backward "
"compatibility
\n
"
,
attrib
);
}
g_free
(
attrib
);
}
else
if
(
!
xmlStrcmp
(
attributes
[
i
],
(
xmlChar
*
)
"id"
))
{
g_free
(
js
->
stream_id
);
js
->
stream_id
=
attrib
;
}
else
{
g_free
(
attrib
);
}
}
if
(
js
->
stream_id
==
NULL
)
{
#if 0
/* This was underspecified in rfc3920 as only being a SHOULD, so
* we cannot rely on it. See #12331 and Oracle's server.
*/
purple_connection_error_reason(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("XMPP stream missing ID"));
#else
/* Instead, let's make up a placeholder stream ID, which we need
* to do because we flag on it being NULL as a special case
* in this parsing code.
*/
js
->
stream_id
=
g_strdup
(
""
);
purple_debug_info
(
"jabber"
,
"Server failed to specify a stream "
"ID (underspecified in rfc3920, but intended "
"to be a MUST; digest legacy auth may fail.
\n
"
);
#endif
}
}
else
{
if
(
js
->
current
)
node
=
xmlnode_new_child
(
js
->
current
,
(
const
char
*
)
element_name
);
else
node
=
xmlnode_new
((
const
char
*
)
element_name
);
xmlnode_set_namespace
(
node
,
(
const
char
*
)
namespace
);
xmlnode_set_prefix
(
node
,
(
const
char
*
)
prefix
);
if
(
nb_namespaces
!=
0
)
{
node
->
namespace_map
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
for
(
i
=
0
,
j
=
0
;
i
<
nb_namespaces
;
i
++
,
j
+=
2
)
{
const
char
*
key
=
(
const
char
*
)
namespaces
[
j
];
const
char
*
val
=
(
const
char
*
)
namespaces
[
j
+
1
];
g_hash_table_insert
(
node
->
namespace_map
,
g_strdup
(
key
?
key
:
""
),
g_strdup
(
val
?
val
:
""
));
}
}
for
(
i
=
0
;
i
<
nb_attributes
*
5
;
i
+=
5
)
{
const
char
*
name
=
(
const
char
*
)
attributes
[
i
];
const
char
*
prefix
=
(
const
char
*
)
attributes
[
i
+
1
];
const
char
*
attrib_ns
=
(
const
char
*
)
attributes
[
i
+
2
];
char
*
txt
;
int
attrib_len
=
attributes
[
i
+
4
]
-
attributes
[
i
+
3
];
char
*
attrib
=
g_strndup
((
gchar
*
)
attributes
[
i
+
3
],
attrib_len
);
txt
=
attrib
;
attrib
=
purple_unescape_text
(
txt
);
g_free
(
txt
);
xmlnode_set_attrib_full
(
node
,
name
,
attrib_ns
,
prefix
,
attrib
);
g_free
(
attrib
);
}
js
->
current
=
node
;
}
}
static
void
jabber_parser_element_end_libxml
(
void
*
user_data
,
const
xmlChar
*
element_name
,
const
xmlChar
*
prefix
,
const
xmlChar
*
namespace
)
{
JabberStream
*
js
=
user_data
;
if
(
!
js
->
current
)
return
;
if
(
js
->
current
->
parent
)
{
if
(
!
xmlStrcmp
((
xmlChar
*
)
js
->
current
->
name
,
element_name
))
js
->
current
=
js
->
current
->
parent
;
}
else
{
xmlnode
*
packet
=
js
->
current
;
js
->
current
=
NULL
;
jabber_process_packet
(
js
,
&
packet
);
if
(
packet
!=
NULL
)
xmlnode_free
(
packet
);
}
}
static
void
jabber_parser_element_text_libxml
(
void
*
user_data
,
const
xmlChar
*
text
,
int
text_len
)
{
JabberStream
*
js
=
user_data
;
if
(
!
js
->
current
)
return
;
if
(
!
text
||
!
text_len
)
return
;
xmlnode_insert_data
(
js
->
current
,
(
const
char
*
)
text
,
text_len
);
}
static
void
jabber_parser_structured_error_handler
(
void
*
user_data
,
xmlErrorPtr
error
)
{
JabberStream
*
js
=
user_data
;
if
(
error
->
level
==
XML_ERR_WARNING
&&
purple_strequal
(
error
->
message
,
"xmlns: URI vcard-temp is not absolute
\n
"
))
/*
* This message happens when parsing vcards, and is normal, so don't
* bother logging it because people scare easily.
*/
return
;
if
(
error
->
level
==
XML_ERR_FATAL
&&
error
->
code
==
XML_ERR_DOCUMENT_END
)
/*
* This is probably more annoying than the vcard-temp error; it occurs
* because we disconnect in most cases without waiting for the receiving
* </stream:stream> (limitations of libpurple)
*/
return
;
purple_debug_error
(
"jabber"
,
"XML parser error for JabberStream %p: "
"Domain %i, code %i, level %i: %s"
,
js
,
error
->
domain
,
error
->
code
,
error
->
level
,
(
error
->
message
?
error
->
message
:
"(null)
\n
"
));
}
static
xmlSAXHandler
jabber_parser_libxml
=
{
NULL
,
/*internalSubset*/
NULL
,
/*isStandalone*/
NULL
,
/*hasInternalSubset*/
NULL
,
/*hasExternalSubset*/
NULL
,
/*resolveEntity*/
NULL
,
/*getEntity*/
NULL
,
/*entityDecl*/
NULL
,
/*notationDecl*/
NULL
,
/*attributeDecl*/
NULL
,
/*elementDecl*/
NULL
,
/*unparsedEntityDecl*/
NULL
,
/*setDocumentLocator*/
NULL
,
/*startDocument*/
NULL
,
/*endDocument*/
NULL
,
/*startElement*/
NULL
,
/*endElement*/
NULL
,
/*reference*/
jabber_parser_element_text_libxml
,
/*characters*/
NULL
,
/*ignorableWhitespace*/
NULL
,
/*processingInstruction*/
NULL
,
/*comment*/
NULL
,
/*warning*/
NULL
,
/*error*/
NULL
,
/*fatalError*/
NULL
,
/*getParameterEntity*/
NULL
,
/*cdataBlock*/
NULL
,
/*externalSubset*/
XML_SAX2_MAGIC
,
/*initialized*/
NULL
,
/*_private*/
jabber_parser_element_start_libxml
,
/*startElementNs*/
jabber_parser_element_end_libxml
,
/*endElementNs*/
jabber_parser_structured_error_handler
/*serror*/
};
void
jabber_parser_setup
(
JabberStream
*
js
)
{
/* This seems backwards, but it makes sense. The libxml code creates
* the parser context when you try to use it (this way, it can figure
* out the encoding at creation time. So, setting up the parser is
* just a matter of destroying any current parser. */
jabber_parser_free
(
js
);
}
void
jabber_parser_free
(
JabberStream
*
js
)
{
if
(
js
->
context
)
{
xmlParseChunk
(
js
->
context
,
NULL
,
0
,
1
);
xmlFreeParserCtxt
(
js
->
context
);
js
->
context
=
NULL
;
}
}
void
jabber_parser_process
(
JabberStream
*
js
,
const
char
*
buf
,
int
len
)
{
int
ret
;
if
(
js
->
context
==
NULL
)
{
/* libxml inconsistently starts parsing on creating the
* parser, so do a ParseChunk right afterwards to force it. */
js
->
context
=
xmlCreatePushParserCtxt
(
&
jabber_parser_libxml
,
js
,
buf
,
len
,
NULL
);
xmlParseChunk
(
js
->
context
,
""
,
0
,
0
);
}
else
if
((
ret
=
xmlParseChunk
(
js
->
context
,
buf
,
len
,
0
))
!=
XML_ERR_OK
)
{
xmlError
*
err
=
xmlCtxtGetLastError
(
js
->
context
);
/*
* libxml2 uses a global setting to determine whether or not to store
* warnings. Other libraries may set this, which causes err to be
* NULL. See #8136 for details.
*/
xmlErrorLevel
level
=
XML_ERR_WARNING
;
if
(
err
)
level
=
err
->
level
;
switch
(
level
)
{
case
XML_ERR_NONE
:
purple_debug_info
(
"jabber"
,
"xmlParseChunk returned info %i
\n
"
,
ret
);
break
;
case
XML_ERR_WARNING
:
purple_debug_warning
(
"jabber"
,
"xmlParseChunk returned warning %i
\n
"
,
ret
);
break
;
case
XML_ERR_ERROR
:
purple_debug_error
(
"jabber"
,
"xmlParseChunk returned error %i
\n
"
,
ret
);
break
;
case
XML_ERR_FATAL
:
purple_debug_error
(
"jabber"
,
"xmlParseChunk returned fatal %i
\n
"
,
ret
);
purple_connection_error_reason
(
js
->
gc
,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
_
(
"XML Parse error"
));
break
;
}
}
if
(
js
->
protocol_version
.
major
==
0
&&
js
->
protocol_version
.
minor
==
9
&&
!
js
->
gc
->
disconnect_timeout
&&
(
js
->
state
==
JABBER_STREAM_INITIALIZING
||
js
->
state
==
JABBER_STREAM_INITIALIZING_ENCRYPTION
))
{
/*
* Legacy servers don't advertise features, so if we've just gotten
* the opening <stream:stream> and there was no version, we need to
* immediately start legacy IQ auth.
*/
jabber_stream_set_state
(
js
,
JABBER_STREAM_AUTHENTICATING
);
jabber_auth_start_old
(
js
);
}
}