pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
The XMPP Console plugin was moved into its own directory. Update POTFILES.in.
kill-oscar-prpl
2019-10-21, John Bailey
27c0aad4a965
The XMPP Console plugin was moved into its own directory. Update POTFILES.in.
/*
* nmconn.c
*
* Copyright (c) 2004 Novell, Inc. All Rights Reserved.
*
* 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; version 2 of the License.
*
* 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
<glib.h>
#include
<unistd.h>
#include
<errno.h>
#include
<string.h>
#include
<ctype.h>
#include
<time.h>
#include
"nmconn.h"
#ifdef _WIN32
#include
<windows.h>
#endif
#include
"util.h"
#define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || \
(ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a))
/* Read data from conn until the end of a line */
static
NMERR_T
read_line
(
NMConn
*
conn
,
char
*
buff
,
int
len
)
{
NMERR_T
rc
=
NM_OK
;
int
total_bytes
=
0
;
while
((
rc
==
NM_OK
)
&&
(
total_bytes
<
(
len
-
1
)))
{
rc
=
nm_read_all
(
conn
,
&
buff
[
total_bytes
],
1
);
if
(
rc
==
NM_OK
)
{
total_bytes
+=
1
;
if
(
buff
[
total_bytes
-
1
]
==
'\n'
)
{
break
;
}
}
}
buff
[
total_bytes
]
=
'\0'
;
return
rc
;
}
static
char
*
url_escape_string
(
char
*
src
)
{
guint32
escape
=
0
;
char
*
p
;
char
*
q
;
char
*
encoded
=
NULL
;
int
ch
;
static
const
char
hex_table
[
16
]
=
"0123456789abcdef"
;
if
(
src
==
NULL
)
{
return
NULL
;
}
/* Find number of chars to escape */
for
(
p
=
src
;
*
p
!=
'\0'
;
p
++
)
{
ch
=
(
guchar
)
*
p
;
if
(
!
NO_ESCAPE
(
ch
))
{
escape
++
;
}
}
encoded
=
g_malloc
((
p
-
src
)
+
(
escape
*
2
)
+
1
);
/* Escape the string */
for
(
p
=
src
,
q
=
encoded
;
*
p
!=
'\0'
;
p
++
)
{
ch
=
(
guchar
)
*
p
;
if
(
NO_ESCAPE
(
ch
))
{
if
(
ch
!=
0x20
)
{
*
q
=
ch
;
q
++
;
}
else
{
*
q
=
'+'
;
q
++
;
}
}
else
{
*
q
=
'%'
;
q
++
;
*
q
=
hex_table
[
ch
>>
4
];
q
++
;
*
q
=
hex_table
[
ch
&
15
];
q
++
;
}
}
*
q
=
'\0'
;
return
encoded
;
}
static
char
*
encode_method
(
guint8
method
)
{
char
*
str
;
switch
(
method
)
{
case
NMFIELD_METHOD_EQUAL
:
str
=
"G"
;
break
;
case
NMFIELD_METHOD_UPDATE
:
str
=
"F"
;
break
;
case
NMFIELD_METHOD_GTE
:
str
=
"E"
;
break
;
case
NMFIELD_METHOD_LTE
:
str
=
"D"
;
break
;
case
NMFIELD_METHOD_NE
:
str
=
"C"
;
break
;
case
NMFIELD_METHOD_EXIST
:
str
=
"B"
;
break
;
case
NMFIELD_METHOD_NOTEXIST
:
str
=
"A"
;
break
;
case
NMFIELD_METHOD_SEARCH
:
str
=
"9"
;
break
;
case
NMFIELD_METHOD_MATCHBEGIN
:
str
=
"8"
;
break
;
case
NMFIELD_METHOD_MATCHEND
:
str
=
"7"
;
break
;
case
NMFIELD_METHOD_NOT_ARRAY
:
str
=
"6"
;
break
;
case
NMFIELD_METHOD_OR_ARRAY
:
str
=
"5"
;
break
;
case
NMFIELD_METHOD_AND_ARRAY
:
str
=
"4"
;
break
;
case
NMFIELD_METHOD_DELETE_ALL
:
str
=
"3"
;
break
;
case
NMFIELD_METHOD_DELETE
:
str
=
"2"
;
break
;
case
NMFIELD_METHOD_ADD
:
str
=
"1"
;
break
;
default
:
/* NMFIELD_METHOD_VALID */
str
=
"0"
;
break
;
}
return
str
;
}
NMConn
*
nm_create_conn
(
const
char
*
addr
,
int
port
)
{
NMConn
*
conn
=
g_new0
(
NMConn
,
1
);
conn
->
addr
=
g_strdup
(
addr
);
conn
->
port
=
port
;
return
conn
;
}
void
nm_release_conn
(
NMConn
*
conn
)
{
if
(
conn
)
{
g_slist_free_full
(
conn
->
requests
,
(
GDestroyNotify
)
nm_release_request
);
conn
->
requests
=
NULL
;
g_free
(
conn
->
ssl_conn
);
conn
->
ssl_conn
=
NULL
;
g_free
(
conn
->
addr
);
conn
->
addr
=
NULL
;
g_free
(
conn
);
}
}
int
nm_tcp_write
(
NMConn
*
conn
,
const
void
*
buff
,
int
len
)
{
if
(
conn
==
NULL
||
buff
==
NULL
)
return
-1
;
if
(
!
conn
->
use_ssl
)
return
(
write
(
conn
->
fd
,
buff
,
len
));
else
if
(
conn
->
ssl_conn
&&
conn
->
ssl_conn
->
write
)
return
(
conn
->
ssl_conn
->
write
(
conn
->
ssl_conn
->
data
,
buff
,
len
));
else
return
-1
;
}
int
nm_tcp_read
(
NMConn
*
conn
,
void
*
buff
,
int
len
)
{
if
(
conn
==
NULL
||
buff
==
NULL
)
return
-1
;
if
(
!
conn
->
use_ssl
)
return
(
read
(
conn
->
fd
,
buff
,
len
));
else
if
(
conn
->
ssl_conn
&&
conn
->
ssl_conn
->
read
)
return
((
conn
->
ssl_conn
->
read
)(
conn
->
ssl_conn
->
data
,
buff
,
len
));
else
return
-1
;
}
NMERR_T
nm_read_all
(
NMConn
*
conn
,
char
*
buff
,
int
len
)
{
NMERR_T
rc
=
NM_OK
;
int
bytes_left
=
len
;
int
bytes_read
;
int
total_bytes
=
0
;
int
retry
=
1000
;
if
(
conn
==
NULL
||
buff
==
NULL
)
return
NMERR_BAD_PARM
;
/* Keep reading until buffer is full */
while
(
bytes_left
)
{
bytes_read
=
nm_tcp_read
(
conn
,
&
buff
[
total_bytes
],
bytes_left
);
if
(
bytes_read
>
0
)
{
bytes_left
-=
bytes_read
;
total_bytes
+=
bytes_read
;
}
else
{
if
(
errno
==
EAGAIN
)
{
if
(
--
retry
==
0
)
{
rc
=
NMERR_TCP_READ
;
break
;
}
#ifdef _WIN32
Sleep
(
1
);
#else
usleep
(
1000
);
#endif
}
else
{
rc
=
NMERR_TCP_READ
;
break
;
}
}
}
return
rc
;
}
NMERR_T
nm_read_uint32
(
NMConn
*
conn
,
guint32
*
val
)
{
NMERR_T
rc
=
NM_OK
;
rc
=
nm_read_all
(
conn
,
(
char
*
)
val
,
sizeof
(
*
val
));
if
(
rc
==
NM_OK
)
{
*
val
=
GUINT32_FROM_LE
(
*
val
);
}
return
rc
;
}
NMERR_T
nm_read_uint16
(
NMConn
*
conn
,
guint16
*
val
)
{
NMERR_T
rc
=
NM_OK
;
rc
=
nm_read_all
(
conn
,
(
char
*
)
val
,
sizeof
(
*
val
));
if
(
rc
==
NM_OK
)
{
*
val
=
GUINT16_FROM_LE
(
*
val
);
}
return
rc
;
}
NMERR_T
nm_write_fields
(
NMConn
*
conn
,
NMField
*
fields
)
{
NMERR_T
rc
=
NM_OK
;
NMField
*
field
;
char
*
value
=
NULL
;
char
*
method
=
NULL
;
char
buffer
[
4096
];
int
ret
;
int
bytes_to_send
;
int
val
=
0
;
if
(
conn
==
NULL
||
fields
==
NULL
)
{
return
NMERR_BAD_PARM
;
}
/* Format each field as valid "post" data and write it out */
for
(
field
=
fields
;
(
rc
==
NM_OK
)
&&
(
field
->
tag
);
field
++
)
{
/* We don't currently handle binary types */
if
(
field
->
method
==
NMFIELD_METHOD_IGNORE
||
field
->
type
==
NMFIELD_TYPE_BINARY
)
{
continue
;
}
/* Write the field tag */
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"&tag=%s"
,
field
->
tag
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
/* Write the field method */
if
(
rc
==
NM_OK
)
{
method
=
encode_method
(
field
->
method
);
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"&cmd=%s"
,
method
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
}
/* Write the field value */
if
(
rc
==
NM_OK
)
{
switch
(
field
->
type
)
{
case
NMFIELD_TYPE_UTF8
:
case
NMFIELD_TYPE_DN
:
value
=
url_escape_string
((
char
*
)
field
->
ptr_value
);
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"&val=%s"
,
value
);
if
(
bytes_to_send
>
(
int
)
sizeof
(
buffer
))
{
ret
=
nm_tcp_write
(
conn
,
buffer
,
sizeof
(
buffer
));
}
else
{
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
}
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
g_free
(
value
);
break
;
case
NMFIELD_TYPE_ARRAY
:
case
NMFIELD_TYPE_MV
:
val
=
nm_count_fields
((
NMField
*
)
field
->
ptr_value
);
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"&val=%u"
,
val
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
break
;
default
:
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"&val=%u"
,
field
->
value
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
break
;
}
}
/* Write the field type */
if
(
rc
==
NM_OK
)
{
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"&type=%u"
,
field
->
type
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
}
/* If the field is a sub array then post its fields */
if
(
rc
==
NM_OK
&&
val
>
0
)
{
if
(
field
->
type
==
NMFIELD_TYPE_ARRAY
||
field
->
type
==
NMFIELD_TYPE_MV
)
{
rc
=
nm_write_fields
(
conn
,
(
NMField
*
)
field
->
ptr_value
);
}
}
}
return
rc
;
}
NMERR_T
nm_send_request
(
NMConn
*
conn
,
char
*
cmd
,
NMField
*
fields
,
nm_response_cb
cb
,
gpointer
data
,
NMRequest
**
request
)
{
NMERR_T
rc
=
NM_OK
;
char
buffer
[
512
];
int
bytes_to_send
;
int
ret
;
NMField
*
request_fields
=
NULL
;
char
*
str
=
NULL
;
if
(
conn
==
NULL
||
cmd
==
NULL
)
return
NMERR_BAD_PARM
;
/* Write the post */
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"POST /%s HTTP/1.0
\r\n
"
,
cmd
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
/* Write headers */
if
(
rc
==
NM_OK
)
{
if
(
purple_strequal
(
"login"
,
cmd
))
{
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"Host: %s:%d
\r\n\r\n
"
,
conn
->
addr
,
conn
->
port
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
}
else
{
bytes_to_send
=
g_snprintf
(
buffer
,
sizeof
(
buffer
),
"
\r\n
"
);
ret
=
nm_tcp_write
(
conn
,
buffer
,
bytes_to_send
);
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
}
}
/* Add the transaction id to the request fields */
if
(
rc
==
NM_OK
)
{
if
(
fields
)
request_fields
=
nm_copy_field_array
(
fields
);
str
=
g_strdup_printf
(
"%d"
,
++
(
conn
->
trans_id
));
request_fields
=
nm_field_add_pointer
(
request_fields
,
NM_A_SZ_TRANSACTION_ID
,
0
,
NMFIELD_METHOD_VALID
,
0
,
str
,
NMFIELD_TYPE_UTF8
);
}
/* Send the request to the server */
if
(
rc
==
NM_OK
)
{
rc
=
nm_write_fields
(
conn
,
request_fields
);
}
/* Write the CRLF to terminate the data */
if
(
rc
==
NM_OK
)
{
ret
=
nm_tcp_write
(
conn
,
"
\r\n
"
,
strlen
(
"
\r\n
"
));
if
(
ret
<
0
)
{
rc
=
NMERR_TCP_WRITE
;
}
}
/* Create a request struct, add it to our queue, and return it */
if
(
rc
==
NM_OK
)
{
NMRequest
*
new_request
=
nm_create_request
(
cmd
,
conn
->
trans_id
,
time
(
0
),
cb
,
NULL
,
data
);
nm_conn_add_request_item
(
conn
,
new_request
);
/* Set the out param if it was sent in, otherwise release the request */
if
(
request
)
*
request
=
new_request
;
else
nm_release_request
(
new_request
);
}
if
(
request_fields
!=
NULL
)
nm_free_fields
(
&
request_fields
);
return
rc
;
}
NMERR_T
nm_read_header
(
NMConn
*
conn
)
{
NMERR_T
rc
=
NM_OK
;
char
buffer
[
512
];
char
*
ptr
=
NULL
;
int
i
;
char
rtn_buf
[
8
];
int
rtn_code
=
0
;
if
(
conn
==
NULL
)
return
NMERR_BAD_PARM
;
*
buffer
=
'\0'
;
rc
=
read_line
(
conn
,
buffer
,
sizeof
(
buffer
));
if
(
rc
==
NM_OK
)
{
/* Find the return code */
ptr
=
strchr
(
buffer
,
' '
);
if
(
ptr
!=
NULL
)
{
ptr
++
;
i
=
0
;
while
(
isdigit
(
*
ptr
)
&&
(
i
<
3
))
{
rtn_buf
[
i
]
=
*
ptr
;
i
++
;
ptr
++
;
}
rtn_buf
[
i
]
=
'\0'
;
if
(
i
>
0
)
rtn_code
=
atoi
(
rtn_buf
);
}
}
/* Finish reading header, in the future we might want to do more processing here */
/* TODO: handle more general redirects in the future */
while
((
rc
==
NM_OK
)
&&
(
!
purple_strequal
(
buffer
,
"
\r\n
"
)))
{
rc
=
read_line
(
conn
,
buffer
,
sizeof
(
buffer
));
}
if
(
rc
==
NM_OK
&&
rtn_code
==
301
)
rc
=
NMERR_SERVER_REDIRECT
;
return
rc
;
}
NMERR_T
nm_read_fields
(
NMConn
*
conn
,
int
count
,
NMField
**
fields
)
{
NMERR_T
rc
=
NM_OK
;
guint8
type
;
guint8
method
;
guint32
val
;
char
tag
[
64
];
NMField
*
sub_fields
=
NULL
;
char
*
str
=
NULL
;
if
(
conn
==
NULL
||
fields
==
NULL
)
return
NMERR_BAD_PARM
;
do
{
if
(
count
>
0
)
{
count
--
;
}
/* Read the field type, method, and tag */
rc
=
nm_read_all
(
conn
,
(
char
*
)
&
type
,
sizeof
(
type
));
if
(
rc
!=
NM_OK
||
type
==
0
)
break
;
rc
=
nm_read_all
(
conn
,
(
char
*
)
&
method
,
sizeof
(
method
));
if
(
rc
!=
NM_OK
)
break
;
rc
=
nm_read_uint32
(
conn
,
&
val
);
if
(
rc
!=
NM_OK
)
break
;
if
(
val
>
sizeof
(
tag
))
{
rc
=
NMERR_PROTOCOL
;
break
;
}
rc
=
nm_read_all
(
conn
,
tag
,
val
);
if
(
rc
!=
NM_OK
)
break
;
if
(
type
==
NMFIELD_TYPE_MV
||
type
==
NMFIELD_TYPE_ARRAY
)
{
/* Read the subarray (first read the number of items in the array) */
rc
=
nm_read_uint32
(
conn
,
&
val
);
if
(
rc
!=
NM_OK
)
break
;
if
(
val
>
0
)
{
rc
=
nm_read_fields
(
conn
,
val
,
&
sub_fields
);
if
(
rc
!=
NM_OK
)
break
;
}
*
fields
=
nm_field_add_pointer
(
*
fields
,
tag
,
0
,
method
,
0
,
sub_fields
,
type
);
sub_fields
=
NULL
;
}
else
if
(
type
==
NMFIELD_TYPE_UTF8
||
type
==
NMFIELD_TYPE_DN
)
{
/* Read the string (first read the length) */
rc
=
nm_read_uint32
(
conn
,
&
val
);
if
(
rc
!=
NM_OK
)
break
;
if
(
val
>=
NMFIELD_MAX_STR_LENGTH
)
{
rc
=
NMERR_PROTOCOL
;
break
;
}
if
(
val
>
0
)
{
str
=
g_new0
(
char
,
val
+
1
);
rc
=
nm_read_all
(
conn
,
str
,
val
);
if
(
rc
!=
NM_OK
)
break
;
*
fields
=
nm_field_add_pointer
(
*
fields
,
tag
,
0
,
method
,
0
,
str
,
type
);
str
=
NULL
;
}
}
else
{
/* Read the numerical value */
rc
=
nm_read_uint32
(
conn
,
&
val
);
if
(
rc
!=
NM_OK
)
break
;
*
fields
=
nm_field_add_number
(
*
fields
,
tag
,
0
,
method
,
0
,
val
,
type
);
}
}
while
(
count
!=
0
);
g_free
(
str
);
if
(
sub_fields
!=
NULL
)
{
nm_free_fields
(
&
sub_fields
);
}
return
rc
;
}
void
nm_conn_add_request_item
(
NMConn
*
conn
,
NMRequest
*
request
)
{
if
(
conn
==
NULL
||
request
==
NULL
)
return
;
nm_request_add_ref
(
request
);
conn
->
requests
=
g_slist_append
(
conn
->
requests
,
request
);
}
void
nm_conn_remove_request_item
(
NMConn
*
conn
,
NMRequest
*
request
)
{
if
(
conn
==
NULL
||
request
==
NULL
)
return
;
conn
->
requests
=
g_slist_remove
(
conn
->
requests
,
request
);
nm_release_request
(
request
);
}
NMRequest
*
nm_conn_find_request
(
NMConn
*
conn
,
int
trans_id
)
{
NMRequest
*
req
=
NULL
;
GSList
*
itr
=
NULL
;
if
(
conn
==
NULL
)
return
NULL
;
itr
=
conn
->
requests
;
while
(
itr
)
{
req
=
(
NMRequest
*
)
itr
->
data
;
if
(
req
!=
NULL
&&
nm_request_get_trans_id
(
req
)
==
trans_id
)
{
return
req
;
}
itr
=
g_slist_next
(
itr
);
}
return
NULL
;
}
const
char
*
nm_conn_get_addr
(
NMConn
*
conn
)
{
if
(
conn
==
NULL
)
return
NULL
;
else
return
conn
->
addr
;
}
int
nm_conn_get_port
(
NMConn
*
conn
)
{
if
(
conn
==
NULL
)
return
-1
;
else
return
conn
->
port
;
}