tizen 2.3 release
[framework/connectivity/bluez.git] / tools / obexctl.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2013  Intel Corporation. All rights reserved.
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdbool.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <signal.h>
34 #include <sys/signalfd.h>
35 #include <inttypes.h>
36
37 #include <readline/readline.h>
38 #include <readline/history.h>
39 #include <glib.h>
40 #include <gdbus.h>
41
42 #include <client/display.h>
43
44 /* String display constants */
45 #define COLORED_NEW     COLOR_GREEN "NEW" COLOR_OFF
46 #define COLORED_CHG     COLOR_YELLOW "CHG" COLOR_OFF
47 #define COLORED_DEL     COLOR_RED "DEL" COLOR_OFF
48
49 #define PROMPT_ON       COLOR_BLUE "[obex]" COLOR_OFF "# "
50 #define PROMPT_OFF      "[obex]# "
51
52 #define OBEX_SESSION_INTERFACE "org.bluez.obex.Session1"
53 #define OBEX_TRANSFER_INTERFACE "org.bluez.obex.Transfer1"
54 #define OBEX_CLIENT_INTERFACE "org.bluez.obex.Client1"
55 #define OBEX_OPP_INTERFACE "org.bluez.obex.ObjectPush1"
56 #define OBEX_FTP_INTERFACE "org.bluez.obex.FileTransfer1"
57 #define OBEX_PBAP_INTERFACE "org.bluez.obex.PhonebookAccess1"
58 #define OBEX_MAP_INTERFACE "org.bluez.obex.MessageAccess1"
59 #define OBEX_MSG_INTERFACE "org.bluez.obex.Message1"
60
61 static GMainLoop *main_loop;
62 static DBusConnection *dbus_conn;
63 static GDBusProxy *default_session;
64 static GSList *sessions = NULL;
65 static GSList *opps = NULL;
66 static GSList *ftps = NULL;
67 static GSList *pbaps = NULL;
68 static GSList *maps = NULL;
69 static GSList *msgs = NULL;
70 static GSList *transfers = NULL;
71 static GDBusProxy *client = NULL;
72
73 struct transfer_data {
74         uint64_t transferred;
75         uint64_t size;
76 };
77
78 static void connect_handler(DBusConnection *connection, void *user_data)
79 {
80         rl_set_prompt(PROMPT_ON);
81         printf("\r");
82         rl_on_new_line();
83         rl_redisplay();
84 }
85
86 static void disconnect_handler(DBusConnection *connection, void *user_data)
87 {
88         rl_set_prompt(PROMPT_OFF);
89         printf("\r");
90         rl_on_new_line();
91         rl_redisplay();
92 }
93
94 static void cmd_quit(int argc, char *argv[])
95 {
96         g_main_loop_quit(main_loop);
97 }
98
99 static void connect_reply(DBusMessage *message, void *user_data)
100 {
101         DBusError error;
102
103         dbus_error_init(&error);
104
105         if (dbus_set_error_from_message(&error, message) == TRUE) {
106                 rl_printf("Failed to connect: %s\n", error.name);
107                 dbus_error_free(&error);
108                 return;
109         }
110
111         rl_printf("Connection successful\n");
112 }
113
114 static void append_variant(DBusMessageIter *iter, int type, void *val)
115 {
116         DBusMessageIter value;
117         char sig[2] = { type, '\0' };
118
119         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
120
121         dbus_message_iter_append_basic(&value, type, val);
122
123         dbus_message_iter_close_container(iter, &value);
124 }
125
126 static void dict_append_entry(DBusMessageIter *dict, const char *key,
127                                                         int type, void *val)
128 {
129         DBusMessageIter entry;
130
131         if (type == DBUS_TYPE_STRING) {
132                 const char *str = *((const char **) val);
133                 if (str == NULL)
134                         return;
135         }
136
137         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
138                                                         NULL, &entry);
139
140         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
141
142         append_variant(&entry, type, val);
143
144         dbus_message_iter_close_container(dict, &entry);
145 }
146
147 struct connect_args {
148         char *dev;
149         char *target;
150 };
151
152 static void connect_args_free(void *data)
153 {
154         struct connect_args *args = data;
155
156         g_free(args->dev);
157         g_free(args->target);
158         g_free(args);
159 }
160
161 static void connect_setup(DBusMessageIter *iter, void *user_data)
162 {
163         struct connect_args *args = user_data;
164         DBusMessageIter dict;
165
166         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->dev);
167
168         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
169                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
170                                         DBUS_TYPE_STRING_AS_STRING
171                                         DBUS_TYPE_VARIANT_AS_STRING
172                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
173                                         &dict);
174
175         if (args->target == NULL)
176                 goto done;
177
178         dict_append_entry(&dict, "Target", DBUS_TYPE_STRING, &args->target);
179
180 done:
181         dbus_message_iter_close_container(iter, &dict);
182 }
183
184 static void cmd_connect(int argc, char *argv[])
185 {
186         struct connect_args *args;
187         const char *target = "opp";
188
189         if (argc < 2) {
190                 rl_printf("Missing device address argument\n");
191                 return;
192         }
193
194         if (!client) {
195                 rl_printf("Client proxy not available\n");
196                 return;
197         }
198
199         if (argc > 2)
200                 target = argv[2];
201
202         args = g_new0(struct connect_args, 1);
203         args->dev = g_strdup(argv[1]);
204         args->target = g_strdup(target);
205
206         if (g_dbus_proxy_method_call(client, "CreateSession", connect_setup,
207                         connect_reply, args, connect_args_free) == FALSE) {
208                 rl_printf("Failed to connect\n");
209                 return;
210         }
211
212         rl_printf("Attempting to connect to %s\n", argv[1]);
213 }
214
215 static void disconnect_reply(DBusMessage *message, void *user_data)
216 {
217         DBusError error;
218
219         dbus_error_init(&error);
220
221         if (dbus_set_error_from_message(&error, message) == TRUE) {
222                 rl_printf("Failed to disconnect: %s\n", error.name);
223                 dbus_error_free(&error);
224                 return;
225         }
226
227         rl_printf("Disconnection successful\n");
228 }
229
230 static void disconnect_setup(DBusMessageIter *iter, void *user_data)
231 {
232         GDBusProxy *proxy = user_data;
233         const char *path;
234
235         path = g_dbus_proxy_get_path(proxy);
236
237         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
238 }
239
240 static GDBusProxy *find_session(const char *path)
241 {
242         GSList *l;
243
244         for (l = sessions; l; l = g_slist_next(l)) {
245                 GDBusProxy *proxy = l->data;
246
247                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
248                         return proxy;
249         }
250
251         return NULL;
252 }
253
254 static void cmd_disconnect(int argc, char *argv[])
255 {
256         GDBusProxy *proxy;
257
258         if (argc > 1)
259                 proxy = find_session(argv[1]);
260         else
261                 proxy = default_session;
262
263         if (proxy == NULL) {
264                 rl_printf("Session not available\n");
265                 return;
266         }
267
268         if (g_dbus_proxy_method_call(client, "RemoveSession", disconnect_setup,
269                                 disconnect_reply, proxy, NULL) == FALSE) {
270                 rl_printf("Failed to disconnect\n");
271                 return;
272         }
273
274         rl_printf("Attempting to disconnect to %s\n",
275                                                 g_dbus_proxy_get_path(proxy));
276 }
277
278 static char *proxy_description(GDBusProxy *proxy, const char *title,
279                                                 const char *description)
280 {
281         const char *path;
282
283         path = g_dbus_proxy_get_path(proxy);
284
285         return g_strdup_printf("%s%s%s%s %s ",
286                                         description ? "[" : "",
287                                         description ? : "",
288                                         description ? "] " : "",
289                                         title, path);
290 }
291
292 static void print_proxy(GDBusProxy *proxy, const char *title,
293                                                         const char *description)
294 {
295         char *str;
296
297         str = proxy_description(proxy, title, description);
298
299         rl_printf("%s%s\n", str, default_session == proxy ? "[default]" : "");
300
301         g_free(str);
302 }
303
304 static void cmd_list(int argc, char *arg[])
305 {
306         GSList *l;
307
308         for (l = sessions; l; l = g_slist_next(l)) {
309                 GDBusProxy *proxy = l->data;
310                 print_proxy(proxy, "Session", NULL);
311         }
312 }
313
314 static bool check_default_session(void)
315 {
316         if (!default_session) {
317                 rl_printf("No default session available\n");
318                 return FALSE;
319         }
320
321         return TRUE;
322 }
323
324 static void print_iter(const char *label, const char *name,
325                                                 DBusMessageIter *iter)
326 {
327         dbus_bool_t valbool;
328         dbus_uint64_t valu64;
329         dbus_uint32_t valu32;
330         dbus_uint16_t valu16;
331         dbus_int16_t vals16;
332         const char *valstr;
333         DBusMessageIter subiter;
334
335         if (iter == NULL) {
336                 rl_printf("%s%s is nil\n", label, name);
337                 return;
338         }
339
340         switch (dbus_message_iter_get_arg_type(iter)) {
341         case DBUS_TYPE_INVALID:
342                 rl_printf("%s%s is invalid\n", label, name);
343                 break;
344         case DBUS_TYPE_STRING:
345         case DBUS_TYPE_OBJECT_PATH:
346                 dbus_message_iter_get_basic(iter, &valstr);
347                 rl_printf("%s%s: %s\n", label, name, valstr);
348                 break;
349         case DBUS_TYPE_BOOLEAN:
350                 dbus_message_iter_get_basic(iter, &valbool);
351                 rl_printf("%s%s: %s\n", label, name,
352                                         valbool == TRUE ? "yes" : "no");
353                 break;
354         case DBUS_TYPE_UINT64:
355                 dbus_message_iter_get_basic(iter, &valu64);
356                 rl_printf("%s%s: %" PRIu64 "\n", label, name, valu64);
357                 break;
358         case DBUS_TYPE_UINT32:
359                 dbus_message_iter_get_basic(iter, &valu32);
360                 rl_printf("%s%s: 0x%08x\n", label, name, valu32);
361                 break;
362         case DBUS_TYPE_UINT16:
363                 dbus_message_iter_get_basic(iter, &valu16);
364                 rl_printf("%s%s: 0x%04x\n", label, name, valu16);
365                 break;
366         case DBUS_TYPE_INT16:
367                 dbus_message_iter_get_basic(iter, &vals16);
368                 rl_printf("%s%s: %d\n", label, name, vals16);
369                 break;
370         case DBUS_TYPE_VARIANT:
371                 dbus_message_iter_recurse(iter, &subiter);
372                 print_iter(label, name, &subiter);
373                 break;
374         case DBUS_TYPE_ARRAY:
375                 dbus_message_iter_recurse(iter, &subiter);
376                 while (dbus_message_iter_get_arg_type(&subiter) !=
377                                                         DBUS_TYPE_INVALID) {
378                         print_iter(label, name, &subiter);
379                         dbus_message_iter_next(&subiter);
380                 }
381                 break;
382         case DBUS_TYPE_DICT_ENTRY:
383                 dbus_message_iter_recurse(iter, &subiter);
384                 dbus_message_iter_get_basic(&subiter, &valstr);
385                 dbus_message_iter_next(&subiter);
386                 print_iter(label, valstr, &subiter);
387                 break;
388         default:
389                 rl_printf("%s%s has unsupported type\n", label, name);
390                 break;
391         }
392 }
393
394 static void print_property(GDBusProxy *proxy, const char *name)
395 {
396         DBusMessageIter iter;
397
398         if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
399                 return;
400
401         print_iter("\t", name, &iter);
402 }
403
404 static void cmd_show(int argc, char *argv[])
405 {
406         GDBusProxy *proxy;
407
408         if (argc < 2) {
409                 if (check_default_session() == FALSE)
410                         return;
411
412                 proxy = default_session;
413         } else {
414                 proxy = find_session(argv[1]);
415                 if (!proxy) {
416                         rl_printf("Session %s not available\n", argv[1]);
417                         return;
418                 }
419         }
420
421         rl_printf("Session %s\n", g_dbus_proxy_get_path(proxy));
422
423         print_property(proxy, "Destination");
424         print_property(proxy, "Target");
425 }
426
427 static void set_default_session(GDBusProxy *proxy)
428 {
429         char *desc;
430         DBusMessageIter iter;
431
432         default_session = proxy;
433
434         if (!g_dbus_proxy_get_property(proxy, "Destination", &iter)) {
435                 desc = g_strdup(PROMPT_ON);
436                 goto done;
437         }
438
439         dbus_message_iter_get_basic(&iter, &desc);
440         desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc);
441
442 done:
443         rl_set_prompt(desc);
444         rl_redisplay();
445         g_free(desc);
446 }
447
448 static void cmd_select(int argc, char *argv[])
449 {
450         GDBusProxy *proxy;
451
452         if (argc < 2) {
453                 rl_printf("Missing session address argument\n");
454                 return;
455         }
456
457         proxy = find_session(argv[1]);
458         if (proxy == NULL) {
459                 rl_printf("Session %s not available\n", argv[1]);
460                 return;
461         }
462
463         if (default_session == proxy)
464                 return;
465
466         set_default_session(proxy);
467
468         print_proxy(proxy, "Session", NULL);
469 }
470
471 static GDBusProxy *find_transfer(const char *path)
472 {
473         GSList *l;
474
475         for (l = transfers; l; l = g_slist_next(l)) {
476                 GDBusProxy *proxy = l->data;
477
478                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
479                         return proxy;
480         }
481
482         return NULL;
483 }
484
485 static GDBusProxy *find_message(const char *path)
486 {
487         GSList *l;
488
489         for (l = msgs; l; l = g_slist_next(l)) {
490                 GDBusProxy *proxy = l->data;
491
492                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
493                         return proxy;
494         }
495
496         return NULL;
497 }
498
499 static void transfer_info(GDBusProxy *proxy, int argc, char *argv[])
500 {
501         rl_printf("Transfer %s\n", g_dbus_proxy_get_path(proxy));
502
503         print_property(proxy, "Session");
504         print_property(proxy, "Name");
505         print_property(proxy, "Type");
506         print_property(proxy, "Status");
507         print_property(proxy, "Time");
508         print_property(proxy, "Size");
509         print_property(proxy, "Transferred");
510         print_property(proxy, "Filename");
511 }
512
513 static void message_info(GDBusProxy *proxy, int argc, char *argv[])
514 {
515         rl_printf("Message %s\n", g_dbus_proxy_get_path(proxy));
516
517         print_property(proxy, "Folder");
518         print_property(proxy, "Subject");
519         print_property(proxy, "Timestamp");
520         print_property(proxy, "Sender");
521         print_property(proxy, "SenderAddress");
522         print_property(proxy, "ReplyTo");
523         print_property(proxy, "Recipient");
524         print_property(proxy, "RecipientAddress");
525         print_property(proxy, "Type");
526         print_property(proxy, "Size");
527         print_property(proxy, "Status");
528         print_property(proxy, "Priority");
529         print_property(proxy, "Read");
530         print_property(proxy, "Deleted");
531         print_property(proxy, "Sent");
532         print_property(proxy, "Protected");
533 }
534
535 static void cmd_info(int argc, char *argv[])
536 {
537         GDBusProxy *proxy;
538
539         if (argc < 2) {
540                 rl_printf("Missing object path argument\n");
541                 return;
542         }
543
544         proxy = find_transfer(argv[1]);
545         if (proxy) {
546                 transfer_info(proxy, argc, argv);
547                 return;
548         }
549
550         proxy = find_message(argv[1]);
551         if (proxy) {
552                 message_info(proxy, argc, argv);
553                 return;
554         }
555
556         rl_printf("Object %s not available\n", argv[1]);
557 }
558
559 static void cancel_reply(DBusMessage *message, void *user_data)
560 {
561         DBusError error;
562
563         dbus_error_init(&error);
564
565         if (dbus_set_error_from_message(&error, message) == TRUE) {
566                 rl_printf("Failed to cancel: %s\n", error.name);
567                 dbus_error_free(&error);
568                 return;
569         }
570
571         rl_printf("Cancel successful\n");
572 }
573
574 static void cmd_cancel(int argc, char *argv[])
575 {
576         GDBusProxy *proxy;
577
578         if (argc < 2) {
579                 rl_printf("Missing transfer address argument\n");
580                 return;
581         }
582
583         proxy = find_transfer(argv[1]);
584         if (!proxy) {
585                 rl_printf("Transfer %s not available\n", argv[1]);
586                 return;
587         }
588
589         if (g_dbus_proxy_method_call(proxy, "Cancel", NULL, cancel_reply, NULL,
590                                                         NULL) == FALSE) {
591                 rl_printf("Failed to cancel transfer\n");
592                 return;
593         }
594
595         rl_printf("Attempting to cancel transfer %s\n",
596                                                 g_dbus_proxy_get_path(proxy));
597 }
598
599 static void suspend_reply(DBusMessage *message, void *user_data)
600 {
601         DBusError error;
602
603         dbus_error_init(&error);
604
605         if (dbus_set_error_from_message(&error, message) == TRUE) {
606                 rl_printf("Failed to suspend: %s\n", error.name);
607                 dbus_error_free(&error);
608                 return;
609         }
610
611         rl_printf("Suspend successful\n");
612 }
613
614 static void cmd_suspend(int argc, char *argv[])
615 {
616         GDBusProxy *proxy;
617
618         if (argc < 2) {
619                 rl_printf("Missing transfer address argument\n");
620                 return;
621         }
622
623         proxy = find_transfer(argv[1]);
624         if (!proxy) {
625                 rl_printf("Transfer %s not available\n", argv[1]);
626                 return;
627         }
628
629         if (g_dbus_proxy_method_call(proxy, "Suspend", NULL, suspend_reply,
630                                                 NULL, NULL) == FALSE) {
631                 rl_printf("Failed to suspend transfer\n");
632                 return;
633         }
634
635         rl_printf("Attempting to suspend transfer %s\n",
636                                                 g_dbus_proxy_get_path(proxy));
637 }
638
639 static void resume_reply(DBusMessage *message, void *user_data)
640 {
641         DBusError error;
642
643         dbus_error_init(&error);
644
645         if (dbus_set_error_from_message(&error, message) == TRUE) {
646                 rl_printf("Failed to resume: %s\n", error.name);
647                 dbus_error_free(&error);
648                 return;
649         }
650
651         rl_printf("Resume successful\n");
652 }
653
654 static void cmd_resume(int argc, char *argv[])
655 {
656         GDBusProxy *proxy;
657
658         if (argc < 2) {
659                 rl_printf("Missing transfer address argument\n");
660                 return;
661         }
662
663         proxy = find_transfer(argv[1]);
664         if (!proxy) {
665                 rl_printf("Transfer %s not available\n", argv[1]);
666                 return;
667         }
668
669         if (g_dbus_proxy_method_call(proxy, "Resume", NULL, resume_reply,
670                                                 NULL, NULL) == FALSE) {
671                 rl_printf("Failed to resume transfer\n");
672                 return;
673         }
674
675         rl_printf("Attempting to resume transfer %s\n",
676                                                 g_dbus_proxy_get_path(proxy));
677 }
678
679 static GDBusProxy *find_opp(const char *path)
680 {
681         GSList *l;
682
683         for (l = opps; l; l = g_slist_next(l)) {
684                 GDBusProxy *proxy = l->data;
685
686                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
687                         return proxy;
688         }
689
690         return NULL;
691 }
692
693 static GDBusProxy *find_map(const char *path)
694 {
695         GSList *l;
696
697         for (l = maps; l; l = g_slist_next(l)) {
698                 GDBusProxy *proxy = l->data;
699
700                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
701                         return proxy;
702         }
703
704         return NULL;
705 }
706
707 static void print_dict_iter(DBusMessageIter *iter)
708 {
709         DBusMessageIter dict;
710         int ctype;
711
712         ctype = dbus_message_iter_get_arg_type(iter);
713         if (ctype != DBUS_TYPE_ARRAY)
714                 return;
715
716         dbus_message_iter_recurse(iter, &dict);
717
718         while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
719                                                         DBUS_TYPE_INVALID) {
720                 DBusMessageIter entry;
721                 const char *key;
722
723                 if (ctype != DBUS_TYPE_DICT_ENTRY)
724                         return;
725
726                 dbus_message_iter_recurse(&dict, &entry);
727                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
728                         return;
729
730                 dbus_message_iter_get_basic(&entry, &key);
731                 dbus_message_iter_next(&entry);
732
733                 print_iter("\t", key, &entry);
734
735                 dbus_message_iter_next(&dict);
736         }
737 }
738
739 static void print_transfer_iter(DBusMessageIter *iter)
740 {
741         const char *path;
742
743         dbus_message_iter_get_basic(iter, &path);
744
745         rl_printf("Transfer %s\n", path);
746
747         dbus_message_iter_next(iter);
748
749         print_dict_iter(iter);
750 }
751
752 static void send_reply(DBusMessage *message, void *user_data)
753 {
754         DBusMessageIter iter;
755         DBusError error;
756
757         dbus_error_init(&error);
758
759         if (dbus_set_error_from_message(&error, message) == TRUE) {
760                 rl_printf("Failed to send: %s\n", error.name);
761                 dbus_error_free(&error);
762                 return;
763         }
764
765         dbus_message_iter_init(message, &iter);
766
767         print_transfer_iter(&iter);
768 }
769
770 static void send_setup(DBusMessageIter *iter, void *user_data)
771 {
772         const char *file = user_data;
773
774         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
775 }
776
777 static void opp_send(GDBusProxy *proxy, int argc, char *argv[])
778 {
779         if (argc < 2) {
780                 rl_printf("Missing file argument\n");
781                 return;
782         }
783
784         if (g_dbus_proxy_method_call(proxy, "SendFile", send_setup, send_reply,
785                                         g_strdup(argv[1]), g_free) == FALSE) {
786                 rl_printf("Failed to send\n");
787                 return;
788         }
789
790         rl_printf("Attempting to send %s to %s\n", argv[1],
791                                                 g_dbus_proxy_get_path(proxy));
792 }
793
794 static void push_reply(DBusMessage *message, void *user_data)
795 {
796         DBusMessageIter iter;
797         DBusError error;
798
799         dbus_error_init(&error);
800
801         if (dbus_set_error_from_message(&error, message) == TRUE) {
802                 rl_printf("Failed to PushMessage: %s\n", error.name);
803                 dbus_error_free(&error);
804                 return;
805         }
806
807         dbus_message_iter_init(message, &iter);
808
809         print_transfer_iter(&iter);
810 }
811
812 static void push_setup(DBusMessageIter *iter, void *user_data)
813 {
814         const char *file = user_data;
815         const char *folder = "";
816         DBusMessageIter dict;
817
818         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
819         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
820
821         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
822                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
823                                         DBUS_TYPE_STRING_AS_STRING
824                                         DBUS_TYPE_VARIANT_AS_STRING
825                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
826                                         &dict);
827
828         dbus_message_iter_close_container(iter, &dict);
829 }
830
831 static void map_send(GDBusProxy *proxy, int argc, char *argv[])
832 {
833         if (argc < 2) {
834                 rl_printf("Missing file argument\n");
835                 return;
836         }
837
838         if (g_dbus_proxy_method_call(proxy, "PushMessage", push_setup,
839                                         push_reply, g_strdup(argv[1]),
840                                         g_free) == FALSE) {
841                 rl_printf("Failed to send\n");
842                 return;
843         }
844
845         rl_printf("Attempting to send %s to %s\n", argv[1],
846                                                 g_dbus_proxy_get_path(proxy));
847 }
848
849 static void cmd_send(int argc, char *argv[])
850 {
851         GDBusProxy *proxy;
852
853         if (!check_default_session())
854                 return;
855
856         proxy = find_opp(g_dbus_proxy_get_path(default_session));
857         if (proxy) {
858                 opp_send(proxy, argc, argv);
859                 return;
860         }
861
862         proxy = find_map(g_dbus_proxy_get_path(default_session));
863         if (proxy) {
864                 map_send(proxy, argc, argv);
865                 return;
866         }
867
868         rl_printf("Command not supported\n");
869 }
870
871 static void change_folder_reply(DBusMessage *message, void *user_data)
872 {
873         DBusError error;
874
875         dbus_error_init(&error);
876
877         if (dbus_set_error_from_message(&error, message) == TRUE) {
878                 rl_printf("Failed to ChangeFolder: %s\n", error.name);
879                 dbus_error_free(&error);
880                 return;
881         }
882
883         rl_printf("ChangeFolder successful\n");
884 }
885
886 static void change_folder_setup(DBusMessageIter *iter, void *user_data)
887 {
888         const char *folder = user_data;
889
890         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
891 }
892
893 static void select_reply(DBusMessage *message, void *user_data)
894 {
895         DBusMessageIter iter;
896         DBusError error;
897
898         dbus_error_init(&error);
899
900         if (dbus_set_error_from_message(&error, message) == TRUE) {
901                 rl_printf("Failed to Select: %s\n", error.name);
902                 dbus_error_free(&error);
903                 return;
904         }
905
906         dbus_message_iter_init(message, &iter);
907
908         rl_printf("Select successful\n");
909 }
910
911 static void select_setup(DBusMessageIter *iter, void *user_data)
912 {
913         const char *folder = user_data;
914         const char *location = "int";
915
916         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &location);
917         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
918 }
919
920 static void setfolder_reply(DBusMessage *message, void *user_data)
921 {
922         DBusError error;
923
924         dbus_error_init(&error);
925
926         if (dbus_set_error_from_message(&error, message) == TRUE) {
927                 rl_printf("Failed to SetFolder: %s\n", error.name);
928                 dbus_error_free(&error);
929                 return;
930         }
931
932         rl_printf("SetFolder successful\n");
933 }
934
935 static void setfolder_setup(DBusMessageIter *iter, void *user_data)
936 {
937         const char *folder = user_data;
938
939         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
940 }
941
942 static GDBusProxy *find_ftp(const char *path)
943 {
944         GSList *l;
945
946         for (l = ftps; l; l = g_slist_next(l)) {
947                 GDBusProxy *proxy = l->data;
948
949                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
950                         return proxy;
951         }
952
953         return NULL;
954 }
955
956 static GDBusProxy *find_pbap(const char *path)
957 {
958         GSList *l;
959
960         for (l = pbaps; l; l = g_slist_next(l)) {
961                 GDBusProxy *proxy = l->data;
962
963                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
964                         return proxy;
965         }
966
967         return NULL;
968 }
969
970 static void ftp_cd(GDBusProxy *proxy, int argc, char *argv[])
971 {
972         if (argc < 2) {
973                 rl_printf("Missing path argument\n");
974                 return;
975         }
976
977         if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup,
978                                         change_folder_reply, g_strdup(argv[1]),
979                                         g_free) == FALSE) {
980                 rl_printf("Failed to ChangeFolder\n");
981                 return;
982         }
983
984         rl_printf("Attempting to ChangeFolder to %s\n", argv[1]);
985 }
986
987 static void pbap_cd(GDBusProxy *proxy, int argc, char *argv[])
988 {
989         if (argc < 2) {
990                 rl_printf("Missing path argument\n");
991                 return;
992         }
993
994         if (g_dbus_proxy_method_call(proxy, "Select", select_setup,
995                                         select_reply, g_strdup(argv[1]),
996                                         g_free) == FALSE) {
997                 rl_printf("Failed to Select\n");
998                 return;
999         }
1000
1001         rl_printf("Attempting to Select to %s\n", argv[1]);
1002 }
1003
1004 static void map_cd(GDBusProxy *proxy, int argc, char *argv[])
1005 {
1006         if (argc < 2) {
1007                 rl_printf("Missing path argument\n");
1008                 return;
1009         }
1010
1011         if (g_dbus_proxy_method_call(proxy, "SetFolder", setfolder_setup,
1012                                         setfolder_reply, g_strdup(argv[1]),
1013                                         g_free) == FALSE) {
1014                 rl_printf("Failed to SetFolder\n");
1015                 return;
1016         }
1017
1018         rl_printf("Attempting to SetFolder to %s\n", argv[1]);
1019 }
1020
1021 static void cmd_cd(int argc, char *argv[])
1022 {
1023         GDBusProxy *proxy;
1024
1025         if (!check_default_session())
1026                 return;
1027
1028         proxy = find_ftp(g_dbus_proxy_get_path(default_session));
1029         if (proxy) {
1030                 ftp_cd(proxy, argc, argv);
1031                 return;
1032         }
1033
1034         proxy = find_pbap(g_dbus_proxy_get_path(default_session));
1035         if (proxy) {
1036                 pbap_cd(proxy, argc, argv);
1037                 return;
1038         }
1039
1040         proxy = find_map(g_dbus_proxy_get_path(default_session));
1041         if (proxy) {
1042                 map_cd(proxy, argc, argv);
1043                 return;
1044         }
1045
1046         rl_printf("Command not supported\n");
1047 }
1048
1049 static void list_folder_reply(DBusMessage *message, void *user_data)
1050 {
1051         DBusMessageIter iter, array;
1052         DBusError error;
1053
1054         dbus_error_init(&error);
1055
1056         if (dbus_set_error_from_message(&error, message) == TRUE) {
1057                 rl_printf("Failed to ListFolder: %s\n", error.name);
1058                 dbus_error_free(&error);
1059                 return;
1060         }
1061
1062         dbus_message_iter_init(message, &iter);
1063
1064         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
1065                 return;
1066
1067         dbus_message_iter_recurse(&iter, &array);
1068
1069         while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1070                 print_dict_iter(&array);
1071                 dbus_message_iter_next(&array);
1072         }
1073 }
1074
1075 static void ftp_ls(GDBusProxy *proxy, int argc, char *argv[])
1076 {
1077         if (g_dbus_proxy_method_call(proxy, "ListFolder", NULL,
1078                                                 list_folder_reply, NULL,
1079                                                 NULL) == FALSE) {
1080                 rl_printf("Failed to ls\n");
1081                 return;
1082         }
1083
1084         rl_printf("Attempting to ListFolder\n");
1085 }
1086
1087 static void parse_list_reply(DBusMessage *message)
1088 {
1089         DBusMessageIter iter, array;
1090
1091         dbus_message_iter_init(message, &iter);
1092
1093         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
1094                 return;
1095
1096         dbus_message_iter_recurse(&iter, &array);
1097
1098         while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1099                 DBusMessageIter entry;
1100                 const char *vcard;
1101
1102                 if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRUCT)
1103                         return;
1104
1105                 dbus_message_iter_recurse(&array, &entry);
1106
1107                 dbus_message_iter_get_basic(&entry, &vcard);
1108                 dbus_message_iter_next(&entry);
1109                 print_iter("\t", vcard, &entry);
1110                 dbus_message_iter_next(&array);
1111         }
1112 }
1113
1114 static void list_reply(DBusMessage *message, void *user_data)
1115 {
1116         DBusError error;
1117
1118         dbus_error_init(&error);
1119
1120         if (dbus_set_error_from_message(&error, message) == TRUE) {
1121                 rl_printf("Failed to List: %s\n", error.name);
1122                 dbus_error_free(&error);
1123                 return;
1124         }
1125
1126         parse_list_reply(message);
1127 }
1128
1129 static void list_setup(DBusMessageIter *iter, void *user_data)
1130 {
1131         DBusMessageIter dict;
1132
1133         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1134                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1135                                         DBUS_TYPE_STRING_AS_STRING
1136                                         DBUS_TYPE_VARIANT_AS_STRING
1137                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1138                                         &dict);
1139
1140         dbus_message_iter_close_container(iter, &dict);
1141 }
1142
1143 static void search_reply(DBusMessage *message, void *user_data)
1144 {
1145         DBusError error;
1146
1147         dbus_error_init(&error);
1148
1149         if (dbus_set_error_from_message(&error, message) == TRUE) {
1150                 rl_printf("Failed to Search: %s\n", error.name);
1151                 dbus_error_free(&error);
1152                 return;
1153         }
1154
1155         parse_list_reply(message);
1156 }
1157
1158 static void search_setup(DBusMessageIter *iter, void *user_data)
1159 {
1160         const char *value = user_data;
1161         const char *field;
1162         DBusMessageIter dict;
1163
1164         field = isalpha(value[0]) ? "name" : "number";
1165
1166         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &field);
1167         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &value);
1168
1169         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1170                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1171                                         DBUS_TYPE_STRING_AS_STRING
1172                                         DBUS_TYPE_VARIANT_AS_STRING
1173                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1174                                         &dict);
1175
1176         dbus_message_iter_close_container(iter, &dict);
1177 }
1178
1179 static void pbap_search(GDBusProxy *proxy, int argc, char *argv[])
1180 {
1181         if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
1182                                         search_reply, g_strdup(argv[1]),
1183                                         g_free) == FALSE) {
1184                 rl_printf("Failed to Search\n");
1185                 return;
1186         }
1187
1188         rl_printf("Attempting to Search\n");
1189 }
1190
1191 static void list_folders_reply(DBusMessage *message, void *user_data)
1192 {
1193         DBusError error;
1194         DBusMessageIter iter, array;
1195
1196         dbus_error_init(&error);
1197
1198         if (dbus_set_error_from_message(&error, message) == TRUE) {
1199                 rl_printf("Failed to ListFolders: %s\n", error.name);
1200                 dbus_error_free(&error);
1201                 return;
1202         }
1203
1204         dbus_message_iter_init(message, &iter);
1205
1206         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
1207                 return;
1208
1209         dbus_message_iter_recurse(&iter, &array);
1210
1211         while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1212                 print_dict_iter(&array);
1213                 dbus_message_iter_next(&array);
1214         }
1215 }
1216
1217 static void list_folders_setup(DBusMessageIter *iter, void *user_data)
1218 {
1219         DBusMessageIter dict;
1220
1221         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1222                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1223                                         DBUS_TYPE_STRING_AS_STRING
1224                                         DBUS_TYPE_VARIANT_AS_STRING
1225                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1226                                         &dict);
1227
1228         dbus_message_iter_close_container(iter, &dict);
1229 }
1230
1231 static void list_messages_reply(DBusMessage *message, void *user_data)
1232 {
1233         DBusError error;
1234         DBusMessageIter iter, array;
1235         int ctype;
1236
1237         dbus_error_init(&error);
1238
1239         if (dbus_set_error_from_message(&error, message) == TRUE) {
1240                 rl_printf("Failed to ListFolders: %s\n", error.name);
1241                 dbus_error_free(&error);
1242                 return;
1243         }
1244
1245         dbus_message_iter_init(message, &iter);
1246
1247         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
1248                 return;
1249
1250         dbus_message_iter_recurse(&iter, &array);
1251
1252         while ((ctype = dbus_message_iter_get_arg_type(&array)) ==
1253                                                         DBUS_TYPE_DICT_ENTRY) {
1254                 DBusMessageIter entry;
1255                 const char *obj;
1256
1257                 dbus_message_iter_recurse(&array, &entry);
1258                 dbus_message_iter_get_basic(&entry, &obj);
1259                 rl_printf("\t%s\n", obj);
1260                 dbus_message_iter_next(&array);
1261         }
1262 }
1263
1264 static void list_messages_setup(DBusMessageIter *iter, void *user_data)
1265 {
1266         const char *folder = user_data;
1267         DBusMessageIter dict;
1268
1269         if (strcmp(folder, "*") == 0)
1270                 folder = "";
1271
1272         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
1273
1274         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1275                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1276                                         DBUS_TYPE_STRING_AS_STRING
1277                                         DBUS_TYPE_VARIANT_AS_STRING
1278                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1279                                         &dict);
1280
1281         dbus_message_iter_close_container(iter, &dict);
1282 }
1283
1284 static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[])
1285 {
1286         if (argc > 1) {
1287                 pbap_search(proxy, argc, argv);
1288                 return;
1289         }
1290
1291         if (g_dbus_proxy_method_call(proxy, "List", list_setup, list_reply,
1292                                                 NULL, NULL) == FALSE) {
1293                 rl_printf("Failed to List\n");
1294                 return;
1295         }
1296
1297         rl_printf("Attempting to List\n");
1298 }
1299
1300 static void map_ls_messages(GDBusProxy *proxy, int argc, char *argv[])
1301 {
1302         if (g_dbus_proxy_method_call(proxy, "ListMessages", list_messages_setup,
1303                                         list_messages_reply, g_strdup(argv[1]),
1304                                         g_free) == FALSE) {
1305                 rl_printf("Failed to ListMessages\n");
1306                 return;
1307         }
1308
1309         rl_printf("Attempting to ListMessages\n");
1310 }
1311
1312 static void map_ls(GDBusProxy *proxy, int argc, char *argv[])
1313 {
1314         if (argc > 1) {
1315                 map_ls_messages(proxy, argc, argv);
1316                 return;
1317         }
1318
1319         if (g_dbus_proxy_method_call(proxy, "ListFolders", list_folders_setup,
1320                                                 list_folders_reply, NULL,
1321                                                 NULL) == FALSE) {
1322                 rl_printf("Failed to ListFolders\n");
1323                 return;
1324         }
1325
1326         rl_printf("Attempting to ListFolders\n");
1327 }
1328
1329 static void cmd_ls(int argc, char *argv[])
1330 {
1331         GDBusProxy *proxy;
1332
1333         if (!check_default_session())
1334                 return;
1335
1336         proxy = find_ftp(g_dbus_proxy_get_path(default_session));
1337         if (proxy) {
1338                 ftp_ls(proxy, argc, argv);
1339                 return;
1340         }
1341
1342         proxy = find_pbap(g_dbus_proxy_get_path(default_session));
1343         if (proxy) {
1344                 pbap_ls(proxy, argc, argv);
1345                 return;
1346         }
1347
1348         proxy = find_map(g_dbus_proxy_get_path(default_session));
1349         if (proxy) {
1350                 map_ls(proxy, argc, argv);
1351                 return;
1352         }
1353
1354         rl_printf("Command not supported\n");
1355 }
1356
1357 struct cp_args {
1358         char *source;
1359         char *target;
1360 };
1361
1362 static void cp_free(void *data)
1363 {
1364         struct cp_args *args = data;
1365
1366         g_free(args->source);
1367         g_free(args->target);
1368         g_free(args);
1369 }
1370
1371 static struct cp_args *cp_new(char *argv[])
1372 {
1373         struct cp_args *args;
1374         const char *source;
1375         const char *target;
1376
1377         source = rindex(argv[1], ':');
1378         if (source == NULL)
1379                 source = argv[1];
1380         else
1381                 source++;
1382
1383         target = rindex(argv[2], ':');
1384         if (target == NULL)
1385                 target = argv[2];
1386         else
1387                 target++;
1388
1389         args = g_new0(struct cp_args, 1);
1390         args->source = g_strdup(source);
1391         args->target = g_strdup(target);
1392
1393         return args;
1394 }
1395
1396 static void cp_setup(DBusMessageIter *iter, void *user_data)
1397 {
1398         struct cp_args *args = user_data;
1399
1400         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->source);
1401         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->target);
1402 }
1403
1404 static void copy_file_reply(DBusMessage *message, void *user_data)
1405 {
1406         DBusError error;
1407
1408         dbus_error_init(&error);
1409
1410         if (dbus_set_error_from_message(&error, message) == TRUE) {
1411                 rl_printf("Failed to CopyFile: %s\n", error.name);
1412                 dbus_error_free(&error);
1413                 return;
1414         }
1415
1416         rl_printf("CopyFile successful\n");
1417 }
1418
1419 static void ftp_copy(GDBusProxy *proxy, int argc, char *argv[])
1420 {
1421         struct cp_args *args;
1422
1423         args = cp_new(argv);
1424
1425         if (g_dbus_proxy_method_call(proxy, "CopyFile", cp_setup,
1426                                 copy_file_reply, args, cp_free) == FALSE) {
1427                 rl_printf("Failed to CopyFile\n");
1428                 return;
1429         }
1430
1431         rl_printf("Attempting to CopyFile\n");
1432 }
1433
1434 static void get_file_reply(DBusMessage *message, void *user_data)
1435 {
1436         DBusError error;
1437         DBusMessageIter iter;
1438
1439         dbus_error_init(&error);
1440
1441         if (dbus_set_error_from_message(&error, message) == TRUE) {
1442                 rl_printf("Failed to GetFile: %s\n", error.name);
1443                 dbus_error_free(&error);
1444                 return;
1445         }
1446
1447         dbus_message_iter_init(message, &iter);
1448
1449         print_transfer_iter(&iter);
1450 }
1451
1452 static void get_file_setup(DBusMessageIter *iter, void *user_data)
1453 {
1454         struct cp_args *args = user_data;
1455
1456         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->target);
1457         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->source);
1458 }
1459
1460 static void ftp_get(GDBusProxy *proxy, int argc, char *argv[])
1461 {
1462         struct cp_args *args;
1463
1464         if (rindex(argv[2], ':') == NULL)
1465                 return ftp_copy(proxy, argc, argv);
1466
1467         args = cp_new(argv);
1468
1469         if (g_dbus_proxy_method_call(proxy, "GetFile", get_file_setup,
1470                                 get_file_reply, args, cp_free) == FALSE) {
1471                 rl_printf("Failed to GetFile\n");
1472                 return;
1473         }
1474
1475         rl_printf("Attempting to GetFile\n");
1476 }
1477
1478 static void put_file_reply(DBusMessage *message, void *user_data)
1479 {
1480         DBusError error;
1481         DBusMessageIter iter;
1482
1483         dbus_error_init(&error);
1484
1485         if (dbus_set_error_from_message(&error, message) == TRUE) {
1486                 rl_printf("Failed to PutFile: %s\n", error.name);
1487                 dbus_error_free(&error);
1488                 return;
1489         }
1490
1491         dbus_message_iter_init(message, &iter);
1492
1493         print_transfer_iter(&iter);
1494 }
1495
1496 static void ftp_put(GDBusProxy *proxy, int argc, char *argv[])
1497 {
1498         struct cp_args *args;
1499
1500         if (rindex(argv[2], ':') != NULL) {
1501                 rl_printf("Invalid target file argument\n");
1502                 return;
1503         }
1504
1505         args = cp_new(argv);
1506
1507         if (g_dbus_proxy_method_call(proxy, "PutFile", cp_setup, put_file_reply,
1508                                                 args, cp_free) == FALSE) {
1509                 rl_printf("Failed to PutFile\n");
1510                 return;
1511         }
1512
1513         rl_printf("Attempting to PutFile\n");
1514 }
1515
1516 static void ftp_cp(GDBusProxy *proxy, int argc, char *argv[])
1517 {
1518         if (argc < 2) {
1519                 rl_printf("Missing source file argument\n");
1520                 return;
1521         }
1522
1523         if (argc < 3) {
1524                 rl_printf("Missing target file argument\n");
1525                 return;
1526         }
1527
1528         if (rindex(argv[1], ':') == NULL)
1529                 return ftp_get(proxy, argc, argv);
1530
1531         return ftp_put(proxy, argc, argv);
1532 }
1533
1534 static void pull_all_reply(DBusMessage *message, void *user_data)
1535 {
1536         DBusError error;
1537
1538         dbus_error_init(&error);
1539
1540         if (dbus_set_error_from_message(&error, message) == TRUE) {
1541                 rl_printf("Failed to PullAll: %s\n", error.name);
1542                 dbus_error_free(&error);
1543                 return;
1544         }
1545
1546
1547         rl_printf("PullAll successful\n");
1548 }
1549
1550 static void pull_all_setup(DBusMessageIter *iter, void *user_data)
1551 {
1552         const char *file = user_data;
1553         DBusMessageIter dict;
1554
1555         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
1556
1557         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1558                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1559                                         DBUS_TYPE_STRING_AS_STRING
1560                                         DBUS_TYPE_VARIANT_AS_STRING
1561                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1562                                         &dict);
1563
1564         dbus_message_iter_close_container(iter, &dict);
1565 }
1566
1567 static void pbap_pull_all(GDBusProxy *proxy, int argc, char *argv[])
1568 {
1569         if (g_dbus_proxy_method_call(proxy, "PullAll", pull_all_setup,
1570                                         pull_all_reply, g_strdup(argv[2]),
1571                                         g_free) == FALSE) {
1572                 rl_printf("Failed to PullAll\n");
1573                 return;
1574         }
1575
1576         rl_printf("Attempting to PullAll\n");
1577 }
1578
1579 static void pull_reply(DBusMessage *message, void *user_data)
1580 {
1581         DBusError error;
1582
1583         dbus_error_init(&error);
1584
1585         if (dbus_set_error_from_message(&error, message) == TRUE) {
1586                 rl_printf("Failed to Pull: %s\n", error.name);
1587                 dbus_error_free(&error);
1588                 return;
1589         }
1590
1591
1592         rl_printf("Pull successful\n");
1593 }
1594
1595 static void pull_setup(DBusMessageIter *iter, void *user_data)
1596 {
1597         struct cp_args *args = user_data;
1598         DBusMessageIter dict;
1599
1600         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->source);
1601         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->target);
1602
1603         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1604                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1605                                         DBUS_TYPE_STRING_AS_STRING
1606                                         DBUS_TYPE_VARIANT_AS_STRING
1607                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1608                                         &dict);
1609
1610         dbus_message_iter_close_container(iter, &dict);
1611 }
1612
1613 static void pbap_pull(GDBusProxy *proxy, int argc, char *argv[])
1614 {
1615         struct cp_args *args;
1616
1617         args = cp_new(argv);
1618
1619         if (g_dbus_proxy_method_call(proxy, "Pull", pull_setup, pull_reply,
1620                                                 args, cp_free) == FALSE) {
1621                 rl_printf("Failed to Pull\n");
1622                 return;
1623         }
1624
1625         rl_printf("Attempting to Pull\n");
1626 }
1627
1628 static void pbap_cp(GDBusProxy *proxy, int argc, char *argv[])
1629 {
1630         if (argc < 2) {
1631                 rl_printf("Missing source file argument\n");
1632                 return;
1633         }
1634
1635         if (argc < 3) {
1636                 rl_printf("Missing target file argument\n");
1637                 return;
1638         }
1639
1640         if (strcmp(argv[1], "*") == 0)
1641                 return pbap_pull_all(proxy, argc, argv);
1642
1643         return pbap_pull(proxy, argc, argv);
1644 }
1645
1646 static void get_reply(DBusMessage *message, void *user_data)
1647 {
1648         DBusMessageIter iter;
1649         DBusError error;
1650
1651         dbus_error_init(&error);
1652
1653         if (dbus_set_error_from_message(&error, message) == TRUE) {
1654                 rl_printf("Failed to Get: %s\n", error.name);
1655                 dbus_error_free(&error);
1656                 return;
1657         }
1658
1659         dbus_message_iter_init(message, &iter);
1660
1661         print_transfer_iter(&iter);
1662 }
1663
1664 static void get_setup(DBusMessageIter *iter, void *user_data)
1665 {
1666         const char *file = user_data;
1667         dbus_bool_t attachment = TRUE;
1668
1669         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
1670         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &attachment);
1671 }
1672
1673 static void map_cp(GDBusProxy *proxy, int argc, char *argv[])
1674 {
1675         GDBusProxy *obj;
1676
1677         if (argc < 2) {
1678                 rl_printf("Missing message argument\n");
1679                 return;
1680         }
1681
1682         obj = find_message(argv[1]);
1683         if (obj == NULL) {
1684                 rl_printf("Invalid message argument\n");
1685                 return;
1686         }
1687
1688         if (argc < 3) {
1689                 rl_printf("Missing target file argument\n");
1690                 return;
1691         }
1692
1693         if (g_dbus_proxy_method_call(obj, "Get", get_setup, get_reply,
1694                                         g_strdup(argv[2]), g_free) == FALSE) {
1695                 rl_printf("Failed to Get\n");
1696                 return;
1697         }
1698
1699         rl_printf("Attempting to Get\n");
1700 }
1701
1702 static void cmd_cp(int argc, char *argv[])
1703 {
1704
1705         GDBusProxy *proxy;
1706
1707         if (!check_default_session())
1708                 return;
1709
1710         proxy = find_ftp(g_dbus_proxy_get_path(default_session));
1711         if (proxy) {
1712                 ftp_cp(proxy, argc, argv);
1713                 return;
1714         }
1715
1716         proxy = find_pbap(g_dbus_proxy_get_path(default_session));
1717         if (proxy) {
1718                 pbap_cp(proxy, argc, argv);
1719                 return;
1720         }
1721
1722         proxy = find_map(g_dbus_proxy_get_path(default_session));
1723         if (proxy) {
1724                 map_cp(proxy, argc, argv);
1725                 return;
1726         }
1727
1728         rl_printf("Command not supported\n");
1729 }
1730
1731 static void move_file_reply(DBusMessage *message, void *user_data)
1732 {
1733         DBusError error;
1734
1735         dbus_error_init(&error);
1736
1737         if (dbus_set_error_from_message(&error, message) == TRUE) {
1738                 rl_printf("Failed to MoveFile: %s\n", error.name);
1739                 dbus_error_free(&error);
1740                 return;
1741         }
1742
1743         rl_printf("MoveFile successful\n");
1744 }
1745
1746 static void cmd_mv(int argc, char *argv[])
1747 {
1748         GDBusProxy *proxy;
1749         struct cp_args *args;
1750
1751         if (!check_default_session())
1752                 return;
1753
1754         if (argc < 2) {
1755                 rl_printf("Missing source file argument\n");
1756                 return;
1757         }
1758
1759         if (argc < 3) {
1760                 rl_printf("Missing target file argument\n");
1761                 return;
1762         }
1763
1764         proxy = find_ftp(g_dbus_proxy_get_path(default_session));
1765         if (proxy == NULL) {
1766                 rl_printf("Command not supported\n");
1767                 return;
1768         }
1769
1770         args = cp_new(argv);
1771
1772         if (g_dbus_proxy_method_call(proxy, "MoveFile", cp_setup,
1773                                 move_file_reply, args, cp_free) == FALSE) {
1774                 rl_printf("Failed to MoveFile\n");
1775                 return;
1776         }
1777
1778         rl_printf("Attempting to MoveFile\n");
1779 }
1780
1781 static void delete_reply(DBusMessage *message, void *user_data)
1782 {
1783         DBusError error;
1784
1785         dbus_error_init(&error);
1786
1787         if (dbus_set_error_from_message(&error, message) == TRUE) {
1788                 rl_printf("Failed to Delete: %s\n", error.name);
1789                 dbus_error_free(&error);
1790                 return;
1791         }
1792
1793         rl_printf("Delete successful\n");
1794 }
1795
1796 static void delete_setup(DBusMessageIter *iter, void *user_data)
1797 {
1798         const char *file = user_data;
1799
1800         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
1801 }
1802
1803 static void ftp_rm(GDBusProxy *proxy, int argc, char *argv[])
1804 {
1805         if (argc < 2) {
1806                 rl_printf("Missing file argument\n");
1807                 return;
1808         }
1809
1810         if (g_dbus_proxy_method_call(proxy, "Delete", delete_setup,
1811                                         delete_reply, g_strdup(argv[1]),
1812                                         g_free) == FALSE) {
1813                 rl_printf("Failed to Delete\n");
1814                 return;
1815         }
1816
1817         rl_printf("Attempting to Delete\n");
1818 }
1819
1820 static void set_delete_reply(const DBusError *error, void *user_data)
1821 {
1822         if (dbus_error_is_set(error))
1823                 rl_printf("Failed to set Deleted: %s\n", error->name);
1824         else
1825                 rl_printf("Set Deleted successful\n");
1826 }
1827
1828 static void map_rm(GDBusProxy *proxy, int argc, char *argv[])
1829 {
1830         GDBusProxy *msg;
1831         dbus_bool_t value = TRUE;
1832
1833         if (argc < 2) {
1834                 rl_printf("Missing message argument\n");
1835                 return;
1836         }
1837
1838         msg = find_message(argv[1]);
1839         if (msg == NULL) {
1840                 rl_printf("Invalid message argument\n");
1841                 return;
1842         }
1843
1844         if (g_dbus_proxy_set_property_basic(msg, "Deleted", DBUS_TYPE_BOOLEAN,
1845                                                 &value, set_delete_reply,
1846                                                 NULL, NULL) == FALSE) {
1847                 rl_printf("Failed to set Deleted\n");
1848                 return;
1849         }
1850
1851         rl_printf("Attempting to set Deleted\n");
1852 }
1853
1854 static void cmd_rm(int argc, char *argv[])
1855 {
1856         GDBusProxy *proxy;
1857
1858         if (!check_default_session())
1859                 return;
1860
1861         proxy = find_ftp(g_dbus_proxy_get_path(default_session));
1862         if (proxy) {
1863                 ftp_rm(proxy, argc, argv);
1864                 return;
1865         }
1866
1867         proxy = find_map(g_dbus_proxy_get_path(default_session));
1868         if (proxy) {
1869                 map_rm(proxy, argc, argv);
1870                 return;
1871         }
1872
1873         rl_printf("Command not supported\n");
1874 }
1875
1876 static void create_folder_reply(DBusMessage *message, void *user_data)
1877 {
1878         DBusError error;
1879
1880         dbus_error_init(&error);
1881
1882         if (dbus_set_error_from_message(&error, message) == TRUE) {
1883                 rl_printf("Failed to CreateFolder: %s\n", error.name);
1884                 dbus_error_free(&error);
1885                 return;
1886         }
1887
1888         rl_printf("CreateFolder successful\n");
1889 }
1890
1891 static void create_folder_setup(DBusMessageIter *iter, void *user_data)
1892 {
1893         const char *folder = user_data;
1894
1895         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
1896 }
1897
1898 static void cmd_mkdir(int argc, char *argv[])
1899 {
1900         GDBusProxy *proxy;
1901
1902         if (!check_default_session())
1903                 return;
1904
1905         if (argc < 2) {
1906                 rl_printf("Missing folder argument\n");
1907                 return;
1908         }
1909
1910         proxy = find_ftp(g_dbus_proxy_get_path(default_session));
1911         if (proxy == NULL) {
1912                 rl_printf("Command not supported\n");
1913                 return;
1914         }
1915
1916         if (g_dbus_proxy_method_call(proxy, "CreateFolder", create_folder_setup,
1917                                         create_folder_reply, g_strdup(argv[1]),
1918                                         g_free) == FALSE) {
1919                 rl_printf("Failed to CreateFolder\n");
1920                 return;
1921         }
1922
1923         rl_printf("Attempting to CreateFolder\n");
1924 }
1925
1926 static const struct {
1927         const char *cmd;
1928         const char *arg;
1929         void (*func) (int argc, char *argv[]);
1930         const char *desc;
1931 } cmd_table[] = {
1932         { "connect",      "<dev> [uuid]", cmd_connect, "Connect session" },
1933         { "disconnect",   "[session]", cmd_disconnect, "Disconnect session" },
1934         { "list",         NULL,       cmd_list, "List available sessions" },
1935         { "show",         "[session]", cmd_show, "Session information" },
1936         { "select",       "<session>", cmd_select, "Select default session" },
1937         { "info",         "<object>", cmd_info, "Object information" },
1938         { "cancel",       "<transfer>", cmd_cancel, "Cancel transfer" },
1939         { "suspend",      "<transfer>", cmd_suspend, "Suspend transfer" },
1940         { "resume",       "<transfer>", cmd_resume, "Resume transfer" },
1941         { "send",         "<file>",   cmd_send, "Send file" },
1942         { "cd",           "<path>",   cmd_cd, "Change current folder" },
1943         { "ls",           NULL,       cmd_ls, "List current folder" },
1944         { "cp",          "<source file> <destination file>",   cmd_cp,
1945                                 "Copy source file to destination file" },
1946         { "mv",          "<source file> <destination file>",   cmd_mv,
1947                                 "Move source file to destination file" },
1948         { "rm",          "<file>",    cmd_rm, "Delete file" },
1949         { "mkdir",       "<folder>",    cmd_mkdir, "Create folder" },
1950         { "quit",         NULL,       cmd_quit, "Quit program" },
1951         { "exit",         NULL,       cmd_quit },
1952         { "help" },
1953         {}
1954 };
1955
1956 static char *cmd_generator(const char *text, int state)
1957 {
1958         static int index, len;
1959         const char *cmd;
1960
1961         if (!state) {
1962                 index = 0;
1963                 len = strlen(text);
1964         }
1965
1966         while ((cmd = cmd_table[index].cmd)) {
1967                 index++;
1968
1969                 if (!strncmp(cmd, text, len))
1970                         return strdup(cmd);
1971         }
1972
1973         return NULL;
1974 }
1975
1976 static char **cmd_completion(const char *text, int start, int end)
1977 {
1978         char **matches = NULL;
1979
1980         if (start == 0) {
1981                 rl_completion_display_matches_hook = NULL;
1982                 matches = rl_completion_matches(text, cmd_generator);
1983         }
1984
1985         if (!matches)
1986                 rl_attempted_completion_over = 1;
1987
1988         return matches;
1989 }
1990
1991 static void rl_handler(char *input)
1992 {
1993         int argc;
1994         char **argv = NULL;
1995         int i;
1996
1997         if (!input) {
1998                 rl_insert_text("quit");
1999                 rl_redisplay();
2000                 rl_crlf();
2001                 g_main_loop_quit(main_loop);
2002                 return;
2003         }
2004
2005         if (!strlen(input))
2006                 goto done;
2007
2008         g_strstrip(input);
2009         add_history(input);
2010
2011         argv = g_strsplit(input, " ", -1);
2012         if (argv == NULL)
2013                 goto done;
2014
2015         for (argc = 0; argv[argc];)
2016                 argc++;
2017
2018         if (argc == 0)
2019                 goto done;
2020
2021         for (i = 0; cmd_table[i].cmd; i++) {
2022                 if (strcmp(argv[0], cmd_table[i].cmd))
2023                         continue;
2024
2025                 if (cmd_table[i].func) {
2026                         cmd_table[i].func(argc, argv);
2027                         goto done;
2028                 }
2029         }
2030
2031         if (strcmp(argv[0], "help")) {
2032                 printf("Invalid command\n");
2033                 goto done;
2034         }
2035
2036         printf("Available commands:\n");
2037
2038         for (i = 0; cmd_table[i].cmd; i++) {
2039                 if (cmd_table[i].desc)
2040                         printf("  %s %-*s %s\n", cmd_table[i].cmd,
2041                                         (int)(25 - strlen(cmd_table[i].cmd)),
2042                                         cmd_table[i].arg ? : "",
2043                                         cmd_table[i].desc ? : "");
2044         }
2045
2046 done:
2047         g_strfreev(argv);
2048         free(input);
2049 }
2050
2051 static gboolean option_version = FALSE;
2052
2053 static GOptionEntry options[] = {
2054         { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
2055                                 "Show version information and exit" },
2056         { NULL },
2057 };
2058
2059 static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
2060                                                         gpointer user_data)
2061 {
2062         static unsigned int __terminated = 0;
2063         struct signalfd_siginfo si;
2064         ssize_t result;
2065         int fd;
2066
2067         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
2068                 g_main_loop_quit(main_loop);
2069                 return FALSE;
2070         }
2071
2072         fd = g_io_channel_unix_get_fd(channel);
2073
2074         result = read(fd, &si, sizeof(si));
2075         if (result != sizeof(si))
2076                 return FALSE;
2077
2078         switch (si.ssi_signo) {
2079         case SIGINT:
2080                 rl_replace_line("", 0);
2081                 rl_crlf();
2082                 rl_on_new_line();
2083                 rl_redisplay();
2084                 break;
2085         case SIGTERM:
2086                 if (__terminated == 0) {
2087                         rl_replace_line("", 0);
2088                         rl_crlf();
2089                         g_main_loop_quit(main_loop);
2090                 }
2091
2092                 __terminated = 1;
2093                 break;
2094         }
2095
2096         return TRUE;
2097 }
2098
2099 static guint setup_signalfd(void)
2100 {
2101         GIOChannel *channel;
2102         guint source;
2103         sigset_t mask;
2104         int fd;
2105
2106         sigemptyset(&mask);
2107         sigaddset(&mask, SIGINT);
2108         sigaddset(&mask, SIGTERM);
2109
2110         if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
2111                 perror("Failed to set signal mask");
2112                 return 0;
2113         }
2114
2115         fd = signalfd(-1, &mask, 0);
2116         if (fd < 0) {
2117                 perror("Failed to create signal descriptor");
2118                 return 0;
2119         }
2120
2121         channel = g_io_channel_unix_new(fd);
2122
2123         g_io_channel_set_close_on_unref(channel, TRUE);
2124         g_io_channel_set_encoding(channel, NULL, NULL);
2125         g_io_channel_set_buffered(channel, FALSE);
2126
2127         source = g_io_add_watch(channel,
2128                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
2129                                 signal_handler, NULL);
2130
2131         g_io_channel_unref(channel);
2132
2133         return source;
2134 }
2135
2136 static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
2137                                                         gpointer user_data)
2138 {
2139         if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
2140                 g_main_loop_quit(main_loop);
2141                 return FALSE;
2142         }
2143
2144         rl_callback_read_char();
2145         return TRUE;
2146 }
2147
2148 static guint setup_standard_input(void)
2149 {
2150         GIOChannel *channel;
2151         guint source;
2152
2153         channel = g_io_channel_unix_new(fileno(stdin));
2154
2155         source = g_io_add_watch(channel,
2156                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
2157                                 input_handler, NULL);
2158
2159         g_io_channel_unref(channel);
2160
2161         return source;
2162 }
2163
2164 static void client_added(GDBusProxy *proxy)
2165 {
2166         if (client == NULL)
2167                 client = proxy;
2168
2169         print_proxy(proxy, "Client", COLORED_NEW);
2170 }
2171
2172 static void session_added(GDBusProxy *proxy)
2173 {
2174         sessions = g_slist_append(sessions, proxy);
2175
2176         if (default_session == NULL)
2177                 set_default_session(proxy);
2178
2179         print_proxy(proxy, "Session", COLORED_NEW);
2180 }
2181
2182 static void print_transferred(struct transfer_data *data, const char *str,
2183                                                         DBusMessageIter *iter)
2184 {
2185         dbus_uint64_t valu64;
2186         uint64_t speed;
2187         int seconds, minutes;
2188
2189         dbus_message_iter_get_basic(iter, &valu64);
2190         speed = valu64 - data->transferred;
2191         data->transferred = valu64;
2192
2193         if (data->size == 0) {
2194                 rl_printf("%sTransferred: %" PRIu64 " (@%" PRIu64 "KB/s)\n",
2195                                                 str, valu64, speed / 1000);
2196                 return;
2197         }
2198
2199         seconds = (data->size - data->transferred) / speed;
2200         minutes = seconds / 60;
2201         seconds %= 60;
2202         rl_printf("%sTransferred: %" PRIu64 " (@%" PRIu64 "KB/s %02u:%02u)\n",
2203                                 str, valu64, speed / 1000, minutes, seconds);
2204 }
2205
2206 static void transfer_property_changed(GDBusProxy *proxy, const char *name,
2207                                         DBusMessageIter *iter, void *user_data)
2208 {
2209         struct transfer_data *data = user_data;
2210         char *str;
2211
2212         str = proxy_description(proxy, "Transfer", COLORED_CHG);
2213
2214         if (strcmp(name, "Transferred") == 0) {
2215                 print_transferred(data, str, iter);
2216                 goto done;
2217         }
2218
2219         if (strcmp(name, "Size") == 0)
2220                 dbus_message_iter_get_basic(iter, &data->size);
2221
2222         print_iter(str, name, iter);
2223
2224 done:
2225         g_free(str);
2226 }
2227
2228 static void transfer_destroy(GDBusProxy *proxy, void *user_data)
2229 {
2230         struct transfer_data *data = user_data;
2231
2232         g_free(data);
2233 }
2234
2235 static void transfer_added(GDBusProxy *proxy)
2236 {
2237         struct transfer_data *data;
2238         DBusMessageIter iter;
2239
2240         transfers = g_slist_append(transfers, proxy);
2241
2242         print_proxy(proxy, "Transfer", COLORED_NEW);
2243
2244         data = g_new0(struct transfer_data, 1);
2245
2246         if (g_dbus_proxy_get_property(proxy, "Transfered", &iter))
2247                 dbus_message_iter_get_basic(&iter, &data->transferred);
2248
2249         if (g_dbus_proxy_get_property(proxy, "Size", &iter))
2250                 dbus_message_iter_get_basic(&iter, &data->size);
2251
2252         g_dbus_proxy_set_property_watch(proxy, transfer_property_changed, data);
2253         g_dbus_proxy_set_removed_watch(proxy, transfer_destroy, data);
2254 }
2255
2256 static void opp_added(GDBusProxy *proxy)
2257 {
2258         opps = g_slist_append(opps, proxy);
2259
2260         print_proxy(proxy, "ObjectPush", COLORED_NEW);
2261 }
2262
2263 static void ftp_added(GDBusProxy *proxy)
2264 {
2265         ftps = g_slist_append(ftps, proxy);
2266
2267         print_proxy(proxy, "FileTransfer", COLORED_NEW);
2268 }
2269
2270 static void pbap_added(GDBusProxy *proxy)
2271 {
2272         pbaps = g_slist_append(pbaps, proxy);
2273
2274         print_proxy(proxy, "PhonebookAccess", COLORED_NEW);
2275 }
2276
2277 static void map_added(GDBusProxy *proxy)
2278 {
2279         maps = g_slist_append(maps, proxy);
2280
2281         print_proxy(proxy, "MessageAccess", COLORED_NEW);
2282 }
2283
2284 static void msg_added(GDBusProxy *proxy)
2285 {
2286         msgs = g_slist_append(msgs, proxy);
2287
2288         print_proxy(proxy, "Message", COLORED_NEW);
2289 }
2290
2291 static void proxy_added(GDBusProxy *proxy, void *user_data)
2292 {
2293         const char *interface;
2294
2295         interface = g_dbus_proxy_get_interface(proxy);
2296
2297         if (!strcmp(interface, OBEX_CLIENT_INTERFACE))
2298                 client_added(proxy);
2299         else if (!strcmp(interface, OBEX_SESSION_INTERFACE))
2300                 session_added(proxy);
2301         else if (!strcmp(interface, OBEX_TRANSFER_INTERFACE))
2302                 transfer_added(proxy);
2303         else if (!strcmp(interface, OBEX_OPP_INTERFACE))
2304                 opp_added(proxy);
2305         else if (!strcmp(interface, OBEX_FTP_INTERFACE))
2306                 ftp_added(proxy);
2307         else if (!strcmp(interface, OBEX_PBAP_INTERFACE))
2308                 pbap_added(proxy);
2309         else if (!strcmp(interface, OBEX_MAP_INTERFACE))
2310                 map_added(proxy);
2311         else if (!strcmp(interface, OBEX_MSG_INTERFACE))
2312                 msg_added(proxy);
2313 }
2314
2315 static void client_removed(GDBusProxy *proxy)
2316 {
2317         print_proxy(proxy, "Client", COLORED_DEL);
2318
2319         if (client == proxy)
2320                 client = NULL;
2321 }
2322
2323 static void session_removed(GDBusProxy *proxy)
2324 {
2325         print_proxy(proxy, "Session", COLORED_DEL);
2326
2327         if (default_session == proxy)
2328                 set_default_session(NULL);
2329
2330         sessions = g_slist_remove(sessions, proxy);
2331 }
2332
2333 static void transfer_removed(GDBusProxy *proxy)
2334 {
2335         print_proxy(proxy, "Transfer", COLORED_DEL);
2336
2337         transfers = g_slist_remove(transfers, proxy);
2338 }
2339
2340 static void opp_removed(GDBusProxy *proxy)
2341 {
2342         print_proxy(proxy, "ObjectPush", COLORED_DEL);
2343
2344         opps = g_slist_remove(opps, proxy);
2345 }
2346
2347 static void ftp_removed(GDBusProxy *proxy)
2348 {
2349         print_proxy(proxy, "FileTransfer", COLORED_DEL);
2350
2351         ftps = g_slist_remove(ftps, proxy);
2352 }
2353
2354 static void pbap_removed(GDBusProxy *proxy)
2355 {
2356         print_proxy(proxy, "PhonebookAccess", COLORED_DEL);
2357
2358         pbaps = g_slist_remove(pbaps, proxy);
2359 }
2360
2361 static void map_removed(GDBusProxy *proxy)
2362 {
2363         print_proxy(proxy, "MessageAccess", COLORED_DEL);
2364
2365         maps = g_slist_remove(maps, proxy);
2366 }
2367
2368 static void msg_removed(GDBusProxy *proxy)
2369 {
2370         print_proxy(proxy, "Message", COLORED_DEL);
2371
2372         msgs = g_slist_remove(msgs, proxy);
2373 }
2374
2375 static void proxy_removed(GDBusProxy *proxy, void *user_data)
2376 {
2377         const char *interface;
2378
2379         interface = g_dbus_proxy_get_interface(proxy);
2380
2381         if (!strcmp(interface, OBEX_CLIENT_INTERFACE))
2382                 client_removed(proxy);
2383         else if (!strcmp(interface, OBEX_SESSION_INTERFACE))
2384                 session_removed(proxy);
2385         else if (!strcmp(interface, OBEX_TRANSFER_INTERFACE))
2386                 transfer_removed(proxy);
2387         else if (!strcmp(interface, OBEX_OPP_INTERFACE))
2388                 opp_removed(proxy);
2389         else if (!strcmp(interface, OBEX_FTP_INTERFACE))
2390                 ftp_removed(proxy);
2391         else if (!strcmp(interface, OBEX_PBAP_INTERFACE))
2392                 pbap_removed(proxy);
2393         else if (!strcmp(interface, OBEX_MAP_INTERFACE))
2394                 map_removed(proxy);
2395         else if (!strcmp(interface, OBEX_MSG_INTERFACE))
2396                 msg_removed(proxy);
2397 }
2398
2399 static void session_property_changed(GDBusProxy *proxy, const char *name,
2400                                                 DBusMessageIter *iter)
2401 {
2402         char *str;
2403
2404         str = proxy_description(proxy, "Session", COLORED_CHG);
2405         print_iter(str, name, iter);
2406         g_free(str);
2407 }
2408
2409 static void property_changed(GDBusProxy *proxy, const char *name,
2410                                         DBusMessageIter *iter, void *user_data)
2411 {
2412         const char *interface;
2413
2414         interface = g_dbus_proxy_get_interface(proxy);
2415
2416         if (!strcmp(interface, OBEX_SESSION_INTERFACE))
2417                 session_property_changed(proxy, name, iter);
2418 }
2419
2420 int main(int argc, char *argv[])
2421 {
2422         GOptionContext *context;
2423         GError *error = NULL;
2424         GDBusClient *client;
2425         guint signal, input;
2426
2427         context = g_option_context_new(NULL);
2428         g_option_context_add_main_entries(context, options, NULL);
2429
2430         if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
2431                 if (error != NULL) {
2432                         g_printerr("%s\n", error->message);
2433                         g_error_free(error);
2434                 } else
2435                         g_printerr("An unknown error occurred\n");
2436                 exit(1);
2437         }
2438
2439         g_option_context_free(context);
2440
2441         if (option_version == TRUE) {
2442                 printf("%s\n", VERSION);
2443                 exit(0);
2444         }
2445
2446         main_loop = g_main_loop_new(NULL, FALSE);
2447         dbus_conn = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
2448
2449         rl_attempted_completion_function = cmd_completion;
2450
2451         rl_erase_empty_line = 1;
2452         rl_callback_handler_install(NULL, rl_handler);
2453
2454         rl_set_prompt(PROMPT_OFF);
2455         rl_redisplay();
2456
2457         input = setup_standard_input();
2458         signal = setup_signalfd();
2459         client = g_dbus_client_new(dbus_conn, "org.bluez.obex",
2460                                                         "/org/bluez/obex");
2461
2462         g_dbus_client_set_connect_watch(client, connect_handler, NULL);
2463         g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
2464
2465         g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
2466                                                         property_changed, NULL);
2467
2468         g_main_loop_run(main_loop);
2469
2470         g_dbus_client_unref(client);
2471         g_source_remove(signal);
2472         g_source_remove(input);
2473
2474         rl_message("");
2475         rl_callback_handler_remove();
2476
2477         dbus_connection_unref(dbus_conn);
2478         g_main_loop_unref(main_loop);
2479
2480         return 0;
2481 }