upgrade obexd to 0.47
[profile/ivi/obexd.git] / tools / test-client.c
1 /*
2  *
3  *  OBEX library with GLib integration
4  *
5  *  Copyright (C) 2011  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <fcntl.h>
25 #include <sys/un.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdio.h>
31
32 #include <readline/readline.h>
33 #include <readline/history.h>
34
35 #include <gobex/gobex.h>
36 #include <btio/btio.h>
37
38 static GMainLoop *main_loop = NULL;
39 static GObex *obex = NULL;
40
41 static gboolean option_packet = FALSE;
42 static gboolean option_bluetooth = FALSE;
43 static char *option_source = NULL;
44 static char *option_dest = NULL;
45 static int option_channel = -1;
46 static int option_imtu = -1;
47 static int option_omtu = -1;
48
49 static void sig_term(int sig)
50 {
51         g_print("Terminating due to signal %d\n", sig);
52         g_main_loop_quit(main_loop);
53 }
54
55 static GOptionEntry options[] = {
56         { "unix", 'u', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
57                         &option_bluetooth, "Use a UNIX socket" },
58         { "bluetooth", 'b', 0, G_OPTION_ARG_NONE,
59                         &option_bluetooth, "Use Bluetooth" },
60         { "source", 's', 0, G_OPTION_ARG_STRING,
61                         &option_source, "Bluetooth adapter address",
62                         "00:..." },
63         { "destination", 'd', 0, G_OPTION_ARG_STRING,
64                         &option_dest, "Remote bluetooth address",
65                         "00:..." },
66         { "channel", 'c', 0, G_OPTION_ARG_INT,
67                         &option_channel, "Transport channel", "CHANNEL" },
68         { "packet", 'p', 0, G_OPTION_ARG_NONE,
69                         &option_packet, "Packet based transport" },
70         { "stream", 's', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
71                         &option_packet, "Stream based transport" },
72         { "input-mtu", 'i', 0, G_OPTION_ARG_INT,
73                         &option_imtu, "Transport input MTU", "MTU" },
74         { "output-mtu", 'o', 0, G_OPTION_ARG_INT,
75                         &option_omtu, "Transport output MTU", "MTU" },
76         { NULL },
77 };
78
79 static void conn_complete(GObex *obex, GError *err, GObexPacket *rsp,
80                                                         gpointer user_data)
81 {
82         if (err != NULL)
83                 g_print("Connect failed: %s\n", err->message);
84         else
85                 g_print("Connect succeeded\n");
86 }
87
88 static void cmd_connect(int argc, char **argv)
89 {
90         g_obex_connect(obex, conn_complete, NULL, NULL, G_OBEX_HDR_INVALID);
91 }
92
93 struct transfer_data {
94         int fd;
95 };
96
97 static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
98 {
99         struct transfer_data *data = user_data;
100
101         if (err != NULL)
102                 g_printerr("failed: %s\n", err->message);
103         else
104                 g_print("transfer succeeded\n");
105
106         close(data->fd);
107         g_free(data);
108 }
109
110 static gssize put_data_cb(void *buf, gsize len, gpointer user_data)
111 {
112         struct transfer_data *data = user_data;
113
114         return read(data->fd, buf, len);
115 }
116
117 static void cmd_put(int argc, char **argv)
118 {
119         struct transfer_data *data;
120         GError *err = NULL;
121         int fd;
122
123         if (argc < 2) {
124                 g_printerr("Filename required\n");
125                 return;
126         }
127
128         fd = open(argv[1], O_RDONLY | O_NOCTTY, 0);
129         if (fd < 0) {
130                 g_printerr("open: %s\n", strerror(errno));
131                 return;
132         }
133
134         data = g_new0(struct transfer_data, 1);
135         data->fd = fd;
136
137         g_obex_put_req(obex, put_data_cb, transfer_complete, data, &err,
138                                                 G_OBEX_HDR_NAME, argv[1],
139                                                 G_OBEX_HDR_INVALID);
140         if (err != NULL) {
141                 g_printerr("put failed: %s\n", err->message);
142                 g_error_free(err);
143                 close(data->fd);
144                 g_free(data);
145         }
146 }
147
148 static gboolean get_data_cb(const void *buf, gsize len, gpointer user_data)
149 {
150         struct transfer_data *data = user_data;
151
152         if (write(data->fd, buf, len) < 0) {
153                 g_printerr("write: %s\n", strerror(errno));
154                 return FALSE;
155         }
156
157         return TRUE;
158 }
159
160 static void cmd_get(int argc, char **argv)
161 {
162         struct transfer_data *data;
163         GError *err = NULL;
164         int fd;
165
166         if (argc < 2) {
167                 g_printerr("Filename required\n");
168                 return;
169         }
170
171         fd = open(argv[1], O_WRONLY | O_CREAT | O_NOCTTY, 0600);
172         if (fd < 0) {
173                 g_printerr("open: %s\n", strerror(errno));
174                 return;
175         }
176
177         data = g_new0(struct transfer_data, 1);
178         data->fd = fd;
179
180         g_obex_get_req(obex, get_data_cb, transfer_complete, data, &err,
181                                                 G_OBEX_HDR_NAME, argv[1],
182                                                 G_OBEX_HDR_INVALID);
183         if (err != NULL) {
184                 g_printerr("get failed: %s\n", err->message);
185                 g_error_free(err);
186                 close(data->fd);
187                 g_free(data);
188         }
189 }
190
191 static void cmd_help(int argc, char **argv);
192
193 static void cmd_exit(int argc, char **argv)
194 {
195         g_main_loop_quit(main_loop);
196 }
197
198 static struct {
199         const char *cmd;
200         void (*func)(int argc, char **argv);
201         const char *params;
202         const char *desc;
203 } commands[] = {
204         { "help",       cmd_help,       "",             "Show this help"},
205         { "exit",       cmd_exit,       "",             "Exit application" },
206         { "quit",       cmd_exit,       "",             "Exit application" },
207         { "connect",    cmd_connect,    "[target]",     "OBEX Connect" },
208         { "put",        cmd_put,        "<file>",       "Send a file" },
209         { "get",        cmd_get,        "<file>",       "Receive a file" },
210         { NULL },
211 };
212
213 static void cmd_help(int argc, char **argv)
214 {
215         int i;
216
217         for (i = 0; commands[i].cmd; i++)
218                 printf("%-15s %-30s %s\n", commands[i].cmd,
219                                 commands[i].params, commands[i].desc);
220 }
221
222 static void parse_line(char *line_read)
223 {
224         gchar **argvp;
225         int argcp;
226         int i;
227
228         if (line_read == NULL) {
229                 g_print("\n");
230                 g_main_loop_quit(main_loop);
231                 return;
232         }
233
234         line_read = g_strstrip(line_read);
235
236         if (*line_read == '\0') {
237                 free(line_read);
238                 return;
239         }
240
241         add_history(line_read);
242
243         g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
244
245         free(line_read);
246
247         for (i = 0; commands[i].cmd; i++)
248                 if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
249                         break;
250
251         if (commands[i].cmd)
252                 commands[i].func(argcp, argvp);
253         else
254                 g_print("%s: command not found\n", argvp[0]);
255
256         g_strfreev(argvp);
257 }
258
259 static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
260                                                         gpointer user_data)
261 {
262         if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
263                 g_main_loop_quit(main_loop);
264                 return FALSE;
265         }
266
267         rl_callback_read_char();
268
269         return TRUE;
270 }
271
272 static void disconn_func(GObex *obex, GError *err, gpointer user_data)
273 {
274         g_printerr("Disconnected: %s\n", err ? err->message : "(no error)");
275         g_main_loop_quit(main_loop);
276 }
277
278 static void transport_connect(GIOChannel *io, GObexTransportType transport)
279 {
280         GIOChannel *input;
281         GIOCondition events;
282
283         g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
284         g_io_channel_set_close_on_unref(io, TRUE);
285
286         obex = g_obex_new(io, transport, option_imtu, option_omtu);
287         g_obex_set_disconnect_function(obex, disconn_func, NULL);
288
289         input = g_io_channel_unix_new(STDIN_FILENO);
290         g_io_channel_set_close_on_unref(input, TRUE);
291         events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
292         g_io_add_watch(input, events, prompt_read, NULL);
293         g_io_channel_unref(input);
294         rl_callback_handler_install("client> ", parse_line);
295 }
296
297 static GIOChannel *unix_connect(GObexTransportType transport)
298 {
299         GIOChannel *io;
300         struct sockaddr_un addr = {
301                 AF_UNIX, "\0/gobex/server"
302         };
303         int sk, err, sock_type;
304
305         if (option_packet)
306                 sock_type = SOCK_SEQPACKET;
307         else
308                 sock_type = SOCK_STREAM;
309
310         sk = socket(PF_LOCAL, sock_type, 0);
311         if (sk < 0) {
312                 err = errno;
313                 g_printerr("Can't create unix socket: %s (%d)\n",
314                                                 strerror(err), err);
315                 return NULL;
316         }
317
318         if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
319                 err = errno;
320                 g_printerr("connect: %s (%d)\n", strerror(err), err);
321                 return NULL;
322         }
323
324         io = g_io_channel_unix_new(sk);
325
326         g_print("Unix socket created: %d\n", sk);
327
328         transport_connect(io, transport);
329
330         return io;
331 }
332
333 static void conn_callback(GIOChannel *io, GError *err, gpointer user_data)
334 {
335         GObexTransportType transport = GPOINTER_TO_UINT(user_data);
336
337         if (err != NULL) {
338                 g_printerr("%s\n", err->message);
339                 return;
340         }
341
342         g_print("Bluetooth socket connected\n");
343
344         transport_connect(io, transport);
345 }
346
347 static GIOChannel *bluetooth_connect(GObexTransportType transport)
348 {
349         GIOChannel *io;
350         GError *err = NULL;
351         BtIOType type;
352         BtIOOption option;
353
354         if (option_dest == NULL || option_channel < 0)
355                 return NULL;
356
357         if (option_channel > 31) {
358                 type = option_packet ? BT_IO_L2CAP : BT_IO_L2ERTM;
359                 option = BT_IO_OPT_PSM;
360         } else {
361                 type = BT_IO_RFCOMM;
362                 option = BT_IO_OPT_CHANNEL;
363         }
364
365         if (option_source) {
366                 if (type == BT_IO_L2CAP) {
367                         io = bt_io_connect(type, conn_callback,
368                                         GUINT_TO_POINTER(transport),
369                                         NULL, &err,
370                                         BT_IO_OPT_SOURCE, option_source,
371                                         BT_IO_OPT_DEST, option_dest,
372                                         option, option_channel,
373                                         BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
374                                         BT_IO_OPT_OMTU, option_omtu,
375                                         BT_IO_OPT_IMTU, option_imtu,
376                                         BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
377                                         BT_IO_OPT_INVALID);
378                 } else {
379                         io = bt_io_connect(type, conn_callback,
380                                         GUINT_TO_POINTER(transport),
381                                         NULL, &err,
382                                         BT_IO_OPT_SOURCE, option_source,
383                                         BT_IO_OPT_DEST, option_dest,
384                                         option, option_channel,
385                                         BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
386                                         BT_IO_OPT_INVALID);
387                 }
388         } else {
389                 if (type == BT_IO_L2CAP) {
390                         io = bt_io_connect(type, conn_callback,
391                                         GUINT_TO_POINTER(transport),
392                                         NULL, &err,
393                                         BT_IO_OPT_DEST, option_dest,
394                                         option, option_channel,
395                                         BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
396                                         BT_IO_OPT_OMTU, option_omtu,
397                                         BT_IO_OPT_IMTU, option_imtu,
398                                         BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
399                                         BT_IO_OPT_INVALID);
400                 } else {
401                         io = bt_io_connect(type, conn_callback,
402                                         GUINT_TO_POINTER(transport),
403                                         NULL, &err,
404                                         BT_IO_OPT_DEST, option_dest,
405                                         option, option_channel,
406                                         BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
407                                         BT_IO_OPT_INVALID);
408                 }
409         }
410
411         if (io != NULL)
412                 return io;
413
414         g_printerr("%s\n", err->message);
415         g_error_free(err);
416         return NULL;
417 }
418
419 int main(int argc, char *argv[])
420 {
421         GOptionContext *context;
422         GError *err = NULL;
423         struct sigaction sa;
424         GIOChannel *io;
425         GObexTransportType transport;
426
427         context = g_option_context_new(NULL);
428         g_option_context_add_main_entries(context, options, NULL);
429
430         g_option_context_parse(context, &argc, &argv, &err);
431         if (err != NULL) {
432                 g_printerr("%s\n", err->message);
433                 g_error_free(err);
434                 exit(EXIT_FAILURE);
435         }
436
437         if (option_packet)
438                 transport = G_OBEX_TRANSPORT_PACKET;
439         else
440                 transport = G_OBEX_TRANSPORT_STREAM;
441
442         if (option_bluetooth)
443                 io = bluetooth_connect(transport);
444         else
445                 io = unix_connect(transport);
446
447         if (io == NULL)
448                 exit(EXIT_FAILURE);
449
450         memset(&sa, 0, sizeof(sa));
451         sa.sa_handler = sig_term;
452         sigaction(SIGINT, &sa, NULL);
453         sigaction(SIGTERM, &sa, NULL);
454
455         main_loop = g_main_loop_new(NULL, FALSE);
456
457         g_main_loop_run(main_loop);
458
459         rl_callback_handler_remove();
460         clear_history();
461         g_obex_unref(obex);
462         g_option_context_free(context);
463         g_main_loop_unref(main_loop);
464
465         exit(EXIT_SUCCESS);
466 }