blob: 122bf4278df4eaed9a0234583716ef94bc740539 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* OBEX library with GLib integration
*
* Copyright (C) 2011 Intel Corporation. All rights reserved.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include "gobex/gobex.h"
#include "util.h"
#define FINAL_BIT 0x80
static GMainLoop *mainloop = NULL;
static uint8_t pkt_connect_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT,
0x00, 0x07, 0x10, 0x00, 0x10, 0x00 };
static uint8_t pkt_connect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07,
0x10, 0x00, 0x10, 0x00 };
static uint8_t pkt_disconnect_req[] = { G_OBEX_OP_DISCONNECT | FINAL_BIT,
0x00, 0x03 };
static uint8_t pkt_disconnect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x03 };
static uint8_t pkt_unauth_rsp[] = { 0x41 | FINAL_BIT, 0x00, 0x1c,
0x10, 0x00, 0x10, 0x00, 0x4d, 0x00,
0x15, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00 };
static uint8_t pkt_auth_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT, 0x00, 0x1c,
0x10, 0x00, 0x10, 0x00, 0x4e, 0x00,
0x15, 0x00, 0x10, 0x5a, 0xd4, 0x93,
0x93, 0xba, 0x4a, 0xf8, 0xac, 0xce,
0x7f, 0x5b, 0x1a, 0x05, 0x38, 0x74,
0x24 };
static uint8_t pkt_auth_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07,
0x10, 0x00, 0x10, 0x00 };
static uint8_t pkt_setpath_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT, 0x00, 0x10,
0x02, 0x00,
G_OBEX_HDR_NAME, 0x00, 0x0b,
0, 'd', 0, 'i', 0, 'r', 0, 0 };
static uint8_t pkt_setpath_up_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT,
0x00, 0x05, 0x03, 0x00 };
static uint8_t pkt_setpath_up_down_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT,
0x00, 0x10, 0x03, 0x00,
G_OBEX_HDR_NAME, 0x00, 0x0b,
0, 'd', 0, 'i', 0, 'r', 0, 0 };
static uint8_t pkt_success_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x03 };
static uint8_t pkt_mkdir_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT, 0x00, 0x10,
0x00, 0x00,
G_OBEX_HDR_NAME, 0x00, 0x0b,
0, 'd', 0, 'i', 0, 'r', 0, 0 };
static uint8_t pkt_delete_req[] = { G_OBEX_OP_PUT | FINAL_BIT, 0x00, 0x16,
G_OBEX_HDR_NAME, 0x00, 0x13,
0, 'f', 0, 'o', 0, 'o', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0 };
static uint8_t pkt_copy_req[] = { G_OBEX_OP_ACTION | FINAL_BIT, 0x00, 0x1b,
G_OBEX_HDR_ACTION, 0x00,
G_OBEX_HDR_NAME, 0x00, 0x0b,
0, 'f', 0, 'o', 0, 'o', 0, 0,
G_OBEX_HDR_DESTNAME, 0x00, 0x0b,
0, 'b', 0, 'a', 0, 'r', 0, 0 };
static uint8_t pkt_move_req[] = { G_OBEX_OP_ACTION | FINAL_BIT, 0x00, 0x1b,
G_OBEX_HDR_ACTION, 0x01,
G_OBEX_HDR_NAME, 0x00, 0x0b,
0, 'f', 0, 'o', 0, 'o', 0, 0,
G_OBEX_HDR_DESTNAME, 0x00, 0x0b,
0, 'b', 0, 'a', 0, 'r', 0, 0 };
static uint8_t pkt_nval_connect_rsp[] = { 0x10 | FINAL_BIT, 0x00, 0x05,
0x10, 0x00, };
static uint8_t pkt_abort_rsp[] = { 0x90, 0x00, 0x03 };
static uint8_t pkt_nval_short_rsp[] = { 0x10 | FINAL_BIT, 0x12 };
static uint8_t pkt_put_body[] = { G_OBEX_OP_PUT, 0x00, 0x0a,
G_OBEX_HDR_BODY, 0x00, 0x07,
1, 2, 3, 4 };
static gboolean timeout(gpointer user_data)
{
struct test_data *d = user_data;
if (!g_main_loop_is_running(mainloop))
return FALSE;
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_TIMEOUT, "Timed out");
d->timer_id = 0;
g_main_loop_quit(mainloop);
return FALSE;
}
static void connect_rsp(GObex *obex, GError *err, GObexPacket *rsp,
gpointer user_data)
{
guint8 rsp_code;
gboolean final;
GError **test_err = user_data;
if (err != NULL) {
g_assert(*test_err == NULL);
*test_err = g_error_copy(err);
goto done;
}
rsp_code = g_obex_packet_get_operation(rsp, &final);
if (rsp_code != 0x20) {
g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Unexpected response 0x%02x", rsp_code);
goto done;
}
if (!final) {
g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Connect response didn't have final bit");
goto done;
}
done:
g_main_loop_quit(mainloop);
}
static void nval_connect_rsp(GObex *obex, GError *err, GObexPacket *rsp,
gpointer user_data)
{
GError **test_err = user_data;
if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR))
g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Did not get expected parse error");
g_main_loop_quit(mainloop);
}
static void timeout_rsp(GObex *obex, GError *err, GObexPacket *rsp,
gpointer user_data)
{
GError **test_err = user_data;
if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT))
g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Did not get expected timeout error");
g_main_loop_quit(mainloop);
}
static gboolean recv_and_send(GIOChannel *io, void *data, gsize len,
GError **err)
{
gsize bytes_written, rbytes;
char buf[255];
GIOStatus status;
status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
if (status != G_IO_STATUS_NORMAL) {
g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"read failed with status %d", status);
return FALSE;
}
if (data == NULL)
return TRUE;
g_io_channel_write_chars(io, data, len, &bytes_written, NULL);
if (bytes_written != len) {
g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Unable to write to socket");
return FALSE;
}
return TRUE;
}
static gboolean send_connect_rsp(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
struct test_data *d = user_data;
if (!recv_and_send(io, pkt_connect_rsp, sizeof(pkt_connect_rsp),
&d->err))
g_main_loop_quit(mainloop);
d->io_id = 0;
return FALSE;
}
static gboolean send_nval_connect_rsp(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
struct test_data *d = user_data;
if (!recv_and_send(io, pkt_nval_connect_rsp,
sizeof(pkt_nval_connect_rsp), &d->err))
g_main_loop_quit(mainloop);
d->io_id = 0;
return FALSE;
}
static gboolean send_nval_short_rsp(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
struct test_data *d = user_data;
if (!recv_and_send(io, pkt_nval_short_rsp,
sizeof(pkt_nval_short_rsp), &d->err))
g_main_loop_quit(mainloop);
d->io_id = 0;
return FALSE;
}
static gboolean send_nothing(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
struct test_data *d = user_data;
if (!recv_and_send(io, NULL, 0, &d->err))
g_main_loop_quit(mainloop);
d->io_id = 0;
return FALSE;
}
static void send_req(GObexPacket *req, GObexResponseFunc rsp_func,
GIOFunc send_rsp_func, int req_timeout,
int transport_type)
{
struct test_data d;
GIOChannel *io;
GIOCondition cond;
int test_time;
GObex *obex;
create_endpoints(&obex, &io, transport_type);
d.err = NULL;
g_obex_send_req(obex, req, req_timeout, rsp_func, &d.err, &d.err);
g_assert_no_error(d.err);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, send_rsp_func, &d);
mainloop = g_main_loop_new(NULL, FALSE);
if (req_timeout > 0)
test_time = req_timeout + 1;
else
test_time = 1;
d.timer_id = g_timeout_add_seconds(test_time, timeout, &d);
g_main_loop_run(mainloop);
g_main_loop_unref(mainloop);
mainloop = NULL;
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
int req_timeout, int transport_type)
{
GObexPacket *req;
guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE, G_OBEX_HDR_INVALID);
g_assert(req != NULL);
g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
G_OBEX_DATA_REF);
send_req(req, rsp_func, send_rsp_func, req_timeout, transport_type);
}
static void test_send_connect_req_stream(void)
{
send_connect(connect_rsp, send_connect_rsp, -1, SOCK_STREAM);
}
static void test_send_connect_req_pkt(void)
{
send_connect(connect_rsp, send_connect_rsp, -1, SOCK_SEQPACKET);
}
static void test_send_nval_connect_req_stream(void)
{
send_connect(nval_connect_rsp, send_nval_connect_rsp, -1, SOCK_STREAM);
}
static void test_send_nval_connect_req_pkt(void)
{
send_connect(nval_connect_rsp, send_nval_connect_rsp, -1,
SOCK_SEQPACKET);
}
static void test_send_nval_connect_req_short_pkt(void)
{
send_connect(nval_connect_rsp, send_nval_short_rsp, -1,
SOCK_SEQPACKET);
}
static void test_send_connect_req_timeout_stream(void)
{
send_connect(timeout_rsp, send_nothing, 0, SOCK_STREAM);
}
static void test_send_connect_req_timeout_pkt(void)
{
send_connect(timeout_rsp, send_nothing, 0, SOCK_SEQPACKET);
}
static void req_done(GObex *obex, GError *err, GObexPacket *rsp,
gpointer user_data)
{
struct test_data *d = user_data;
if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED))
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Did not get expected cancelled error");
g_main_loop_quit(mainloop);
}
static void test_cancel_req_immediate(void)
{
GObexPacket *req;
struct test_data d;
gboolean ret;
create_endpoints(&d.obex, NULL, SOCK_STREAM);
d.err = NULL;
req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_INVALID);
d.id = g_obex_send_req(d.obex, req, -1, req_done, &d, &d.err);
g_assert_no_error(d.err);
g_assert(d.id != 0);
ret = g_obex_cancel_req(d.obex, d.id, FALSE);
g_assert(ret == TRUE);
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
g_assert_no_error(d.err);
g_obex_unref(d.obex);
g_main_loop_unref(mainloop);
}
static gboolean cancel_server(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
struct test_data *d = user_data;
GIOStatus status;
gsize bytes_written, rbytes;
char buf[255];
status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
if (status != G_IO_STATUS_NORMAL) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Reading data failed with status %d", status);
goto failed;
}
if (rbytes < 3) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Not enough data from socket");
goto failed;
}
if ((uint8_t) buf[0] == (G_OBEX_OP_PUT | FINAL_BIT)) {
if (!g_obex_cancel_req(d->obex, d->id, FALSE)) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Cancelling request failed");
goto failed;
}
return TRUE;
}
if ((uint8_t) buf[0] != (G_OBEX_OP_ABORT | FINAL_BIT)) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Neither Put nor Abort packet received");
goto failed;
}
g_io_channel_write_chars(io, (char *) pkt_abort_rsp,
sizeof(pkt_abort_rsp), &bytes_written, NULL);
if (bytes_written != sizeof(pkt_abort_rsp)) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Unable to write to socket");
goto failed;
}
return TRUE;
failed:
d->io_id = 0;
g_main_loop_quit(mainloop);
return FALSE;
}
static void test_cancel_req_delay(int transport_type)
{
GIOChannel *io;
struct test_data d;
GObexPacket *req;
GIOCondition cond;
create_endpoints(&d.obex, &io, transport_type);
d.err = NULL;
req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_INVALID);
d.id = g_obex_send_req(d.obex, req, -1, req_done, &d, &d.err);
g_assert_no_error(d.err);
g_assert(d.id != 0);
mainloop = g_main_loop_new(NULL, FALSE);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, cancel_server, &d);
d.timer_id = g_timeout_add_seconds(2, timeout, &d);
g_main_loop_run(mainloop);
g_assert_no_error(d.err);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(d.obex);
g_main_loop_unref(mainloop);
}
static void test_cancel_req_delay_stream(void)
{
test_cancel_req_delay(SOCK_STREAM);
}
static void test_cancel_req_delay_pkt(void)
{
test_cancel_req_delay(SOCK_SEQPACKET);
}
static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data)
{
struct test_data *d = user_data;
struct test_buf *b = d->recv;
GIOStatus status;
gsize rbytes;
char buf[255];
if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Unexpected condition %d on socket", cond);
goto done;
}
status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
if (status != G_IO_STATUS_NORMAL) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Reading data failed with status %d", status);
goto done;
}
if ((gssize) rbytes != b->len) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Got %zu bytes instead of %zu",
rbytes, sizeof(pkt_connect_req));
dump_bufs(b->data, b->len, buf, rbytes);
goto done;
}
if (memcmp(buf, b->data, rbytes) != 0) {
g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Mismatch with received data");
dump_bufs(b->data, b->len, buf, rbytes);
goto done;
}
done:
d->io_id = 0;
g_main_loop_quit(mainloop);
return FALSE;
}
static void test_send_connect(int transport_type)
{
guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
struct test_data d;
struct test_buf *b = d.recv;
GIOChannel *io;
GIOCondition cond;
GObexPacket *req;
GObex *obex;
create_endpoints(&obex, &io, transport_type);
memset(&d, 0, sizeof(d));
b->data = pkt_connect_req;
b->len = sizeof(pkt_connect_req);
req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE, G_OBEX_HDR_INVALID);
g_assert(req != NULL);
g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
G_OBEX_DATA_REF);
g_obex_send(obex, req, &d.err);
g_assert_no_error(d.err);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, rcv_data, &d);
mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, timeout, &d);
g_main_loop_run(mainloop);
g_main_loop_unref(mainloop);
mainloop = NULL;
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_send_connect_stream(void)
{
test_send_connect(SOCK_STREAM);
}
static void test_send_connect_pkt(void)
{
test_send_connect(SOCK_SEQPACKET);
}
static void unexpected_disconn(GObex *obex, GError *err, gpointer user_data)
{
GError **test_err = user_data;
if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR))
g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Didn't get parse error as expected");
g_main_loop_quit(mainloop);
}
static void test_recv_unexpected(void)
{
struct test_data d;
GObexPacket *req;
GIOChannel *io;
GObex *obex;
guint8 buf[255];
gssize len;
create_endpoints(&obex, &io, SOCK_STREAM);
d.err = NULL;
g_obex_set_disconnect_function(obex, unexpected_disconn, &d.err);
req = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID);
len = g_obex_packet_encode(req, buf, sizeof(buf));
g_obex_packet_free(req);
g_assert_cmpint(len, >=, 0);
g_io_channel_write_chars(io, (char *) buf, len, NULL, &d.err);
g_assert_no_error(d.err);
mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, timeout, &d);
g_main_loop_run(mainloop);
g_main_loop_unref(mainloop);
mainloop = NULL;
if (d.timer_id > 0)
g_source_remove(d.timer_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static gssize get_body_data(void *buf, gsize len, gpointer user_data)
{
uint8_t data[] = { 1, 2, 3, 4 };
memcpy(buf, data, sizeof(data));
return sizeof(data);
}
static gssize get_body_data_fail(void *buf, gsize len, gpointer user_data)
{
g_main_loop_quit(mainloop);
return -1;
}
static void test_send_on_demand(int transport_type, GObexDataProducer func)
{
struct test_data d;
struct test_buf *b = d.recv;
GIOChannel *io;
GIOCondition cond;
GObexPacket *req;
GObex *obex;
create_endpoints(&obex, &io, transport_type);
memset(&d, 0, sizeof(d));
b->data = pkt_put_body;
b->len = sizeof(pkt_put_body);
req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
g_obex_packet_add_body(req, func, &d);
g_obex_send(obex, req, &d.err);
g_assert_no_error(d.err);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, rcv_data, &d);
mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, timeout, &d);
g_main_loop_run(mainloop);
g_main_loop_unref(mainloop);
mainloop = NULL;
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_send_on_demand_stream(void)
{
test_send_on_demand(SOCK_STREAM, get_body_data);
}
static void test_send_on_demand_pkt(void)
{
test_send_on_demand(SOCK_SEQPACKET, get_body_data);
}
static void test_send_on_demand_fail_stream(void)
{
test_send_on_demand(SOCK_STREAM, get_body_data_fail);
}
static void test_send_on_demand_fail_pkt(void)
{
test_send_on_demand(SOCK_SEQPACKET, get_body_data_fail);
}
static void handle_connect_req(GObex *obex, GObexPacket *req,
gpointer user_data)
{
GError **test_err = user_data;
if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_CONNECT)
g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Unexpected operation");
g_main_loop_quit(mainloop);
}
static void handle_connect_err(GObex *obex, GError *err, gpointer user_data)
{
GError **test_err = user_data;
g_main_loop_quit(mainloop);
if (err != NULL)
*test_err = g_error_copy(err);
else
*test_err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Disconnected");
}
static void recv_connect(int transport_type)
{
struct test_data d;
GObex *obex;
GIOChannel *io;
GIOStatus status;
gsize bytes_written;
create_endpoints(&obex, &io, transport_type);
d.err = NULL;
g_obex_add_request_function(obex, G_OBEX_OP_CONNECT,
handle_connect_req, &d.err);
g_obex_set_disconnect_function(obex, handle_connect_err, &d.err);
status = g_io_channel_write_chars(io, (char *) pkt_connect_req,
sizeof(pkt_connect_req),
&bytes_written, NULL);
g_assert_cmpint(status, ==, G_IO_STATUS_NORMAL);
g_assert_cmpuint(bytes_written, ==, sizeof(pkt_connect_req));
mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, timeout, &d);
g_main_loop_run(mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
g_obex_unref(obex);
g_io_channel_unref(io);
g_main_loop_unref(mainloop);
mainloop = NULL;
g_assert_no_error(d.err);
}
static void test_recv_connect_stream(void)
{
recv_connect(SOCK_STREAM);
}
static void test_recv_connect_pkt(void)
{
recv_connect(SOCK_SEQPACKET);
}
static void disconn_ev(GObex *obex, GError *err, gpointer user_data)
{
GError **test_err = user_data;
if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED))
g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
"Did not get expected disconnect error");
g_main_loop_quit(mainloop);
}
static void test_disconnect(void)
{
struct test_data d;
GObex *obex;
GIOChannel *io;
create_endpoints(&obex, &io, SOCK_STREAM);
d.err = NULL;
g_obex_set_disconnect_function(obex, disconn_ev, &d.err);
d.timer_id = g_timeout_add_seconds(1, timeout, &d);
mainloop = g_main_loop_new(NULL, FALSE);
g_io_channel_shutdown(io, FALSE, NULL);
g_main_loop_run(mainloop);
g_assert_no_error(d.err);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_main_loop_unref(mainloop);
mainloop = NULL;
}
static void test_ref_unref(void)
{
GObex *obex;
obex = create_gobex(STDIN_FILENO, G_OBEX_TRANSPORT_STREAM, FALSE);
g_assert(obex != NULL);
obex = g_obex_ref(obex);
g_obex_unref(obex);
g_obex_unref(obex);
}
static void test_basic(void)
{
GObex *obex;
obex = create_gobex(STDIN_FILENO, G_OBEX_TRANSPORT_STREAM, FALSE);
g_assert(obex != NULL);
g_obex_unref(obex);
}
static void test_null_io(void)
{
GObex *obex;
obex = g_obex_new(NULL, 0, -1, -1);
g_assert(obex == NULL);
}
static void req_complete(GObex *obex, GError *err, GObexPacket *rsp,
gpointer user_data)
{
struct test_data *d = user_data;
if (err != NULL)
d->err = g_error_copy(err);
g_main_loop_quit(d->mainloop);
}
static void test_connect(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_connect_req, sizeof(pkt_connect_req) } }, {
{ pkt_connect_rsp, sizeof(pkt_connect_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_obex_disconnect(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_disconnect_req, sizeof(pkt_disconnect_req) } }, {
{ pkt_disconnect_rsp, sizeof(pkt_disconnect_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_disconnect(obex, req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_auth(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_connect_req, sizeof(pkt_connect_req) },
{ pkt_auth_req, sizeof(pkt_auth_req) } }, {
{ pkt_unauth_rsp, sizeof(pkt_unauth_rsp) },
{ pkt_auth_rsp, sizeof(pkt_auth_rsp) } },
};
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 2);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_auth_fail(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_connect_req, sizeof(pkt_connect_req) },
{ pkt_auth_req, sizeof(pkt_auth_req) } }, {
{ pkt_unauth_rsp, sizeof(pkt_unauth_rsp) },
{ pkt_unauth_rsp, sizeof(pkt_unauth_rsp) } },
};
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 2);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_setpath(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_setpath_req, sizeof(pkt_setpath_req) } }, {
{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_setpath(obex, "dir", req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_setpath_up(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_setpath_up_req, sizeof(pkt_setpath_up_req) } }, {
{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_setpath(obex, "..", req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_setpath_up_down(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_setpath_up_down_req,
sizeof(pkt_setpath_up_down_req) } }, {
{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_setpath(obex, "../dir", req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_mkdir(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_mkdir_req, sizeof(pkt_mkdir_req) } }, {
{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_mkdir(obex, "dir", req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_delete(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_delete_req, sizeof(pkt_delete_req) } }, {
{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_delete(obex, "foo.txt", req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_copy(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_copy_req, sizeof(pkt_copy_req) } }, {
{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_copy(obex, "foo", "bar", req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
static void test_move(void)
{
GIOChannel *io;
GIOCondition cond;
GObex *obex;
struct test_data d = { 0, NULL, {
{ pkt_move_req, sizeof(pkt_move_req) } }, {
{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
d.io_id = g_io_add_watch(io, cond, test_io_cb, &d);
d.mainloop = g_main_loop_new(NULL, FALSE);
d.timer_id = g_timeout_add_seconds(1, test_timeout, &d);
g_obex_move(obex, "foo", "bar", req_complete, &d, &d.err);
g_assert_no_error(d.err);
g_main_loop_run(d.mainloop);
g_assert_cmpuint(d.count, ==, 1);
g_main_loop_unref(d.mainloop);
if (d.timer_id > 0)
g_source_remove(d.timer_id);
if (d.io_id > 0)
g_source_remove(d.io_id);
g_io_channel_unref(io);
g_obex_unref(obex);
g_assert_no_error(d.err);
}
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/gobex/null_io", test_null_io);
g_test_add_func("/gobex/basic", test_basic);
g_test_add_func("/gobex/ref_unref", test_ref_unref);
g_test_add_func("/gobex/test_disconnect", test_disconnect);
g_test_add_func("/gobex/test_recv_connect_stream",
test_recv_connect_stream);
g_test_add_func("/gobex/test_recv_connect_pkt",
test_recv_connect_pkt);
g_test_add_func("/gobex/test_send_connect_stream",
test_send_connect_stream);
g_test_add_func("/gobex/test_send_connect_pkt",
test_send_connect_pkt);
g_test_add_func("/gobex/test_recv_unexpected",
test_recv_unexpected);
g_test_add_func("/gobex/test_send_on_demand_stream",
test_send_on_demand_stream);
g_test_add_func("/gobex/test_send_on_demand_pkt",
test_send_on_demand_pkt);
g_test_add_func("/gobex/test_send_on_demand_fail_stream",
test_send_on_demand_fail_stream);
g_test_add_func("/gobex/test_send_on_demand_fail_pkt",
test_send_on_demand_fail_pkt);
g_test_add_func("/gobex/test_send_connect_req_stream",
test_send_connect_req_stream);
g_test_add_func("/gobex/test_send_connect_req_pkt",
test_send_connect_req_pkt);
g_test_add_func("/gobex/test_send_nval_connect_req_stream",
test_send_nval_connect_req_stream);
g_test_add_func("/gobex/test_send_nval_connect_req_pkt",
test_send_nval_connect_req_pkt);
g_test_add_func("/gobex/test_send_nval_connect_req_short_pkt",
test_send_nval_connect_req_short_pkt);
g_test_add_func("/gobex/test_send_connect_req_timeout_stream",
test_send_connect_req_timeout_stream);
g_test_add_func("/gobex/test_send_connect_req_timeout_pkt",
test_send_connect_req_timeout_pkt);
g_test_add_func("/gobex/test_cancel_req_immediate",
test_cancel_req_immediate);
g_test_add_func("/gobex/test_cancel_req_delay_stream",
test_cancel_req_delay_stream);
g_test_add_func("/gobex/test_cancel_req_delay_pkt",
test_cancel_req_delay_pkt);
g_test_add_func("/gobex/test_connect", test_connect);
g_test_add_func("/gobex/test_obex_disconnect", test_obex_disconnect);
g_test_add_func("/gobex/test_auth", test_auth);
g_test_add_func("/gobex/test_auth_fail", test_auth_fail);
g_test_add_func("/gobex/test_setpath", test_setpath);
g_test_add_func("/gobex/test_setpath_up", test_setpath_up);
g_test_add_func("/gobex/test_setpath_up_down", test_setpath_up_down);
g_test_add_func("/gobex/test_mkdir", test_mkdir);
g_test_add_func("/gobex/test_delete", test_delete);
g_test_add_func("/gobex/test_copy", test_copy);
g_test_add_func("/gobex/test_move", test_move);
return g_test_run();
}