upgrade obexd to 0.47
[profile/ivi/obexd.git] / client / ftp.c
1 /*
2  *
3  *  OBEX Client
4  *
5  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
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 <errno.h>
29 #include <string.h>
30
31 #include <gdbus.h>
32
33 #include "dbus.h"
34 #include "log.h"
35
36 #include "transfer.h"
37 #include "session.h"
38 #include "driver.h"
39 #include "ftp.h"
40
41 #define OBEX_FTP_UUID \
42         "\xF9\xEC\x7B\xC4\x95\x3C\x11\xD2\x98\x4E\x52\x54\x00\xDC\x9E\x09"
43 #define OBEX_FTP_UUID_LEN 16
44
45 #define FTP_INTERFACE "org.bluez.obex.FileTransfer"
46 #define ERROR_INTERFACE "org.bluez.obex.Error"
47 #define FTP_UUID "00001106-0000-1000-8000-00805f9b34fb"
48 #define PCSUITE_UUID "00005005-0000-1000-8000-0002ee000001"
49
50 static DBusConnection *conn = NULL;
51
52 struct ftp_data {
53         struct obc_session *session;
54 };
55
56 static void async_cb(struct obc_session *session, struct obc_transfer *transfer,
57                                                 GError *err, void *user_data)
58 {
59         DBusMessage *reply, *msg = user_data;
60
61         if (err != NULL)
62                 reply = g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
63                                                 "%s", err->message);
64         else
65                 reply = dbus_message_new_method_return(msg);
66
67         g_dbus_send_message(conn, reply);
68         dbus_message_unref(msg);
69 }
70
71 static DBusMessage *change_folder(DBusConnection *connection,
72                                 DBusMessage *message, void *user_data)
73 {
74         struct ftp_data *ftp = user_data;
75         struct obc_session *session = ftp->session;
76         const char *folder;
77         GError *err = NULL;
78
79         if (dbus_message_get_args(message, NULL,
80                                 DBUS_TYPE_STRING, &folder,
81                                 DBUS_TYPE_INVALID) == FALSE)
82                 return g_dbus_create_error(message,
83                                 ERROR_INTERFACE ".InvalidArguments", NULL);
84
85         obc_session_setpath(session, folder, async_cb, message, &err);
86         if (err != NULL) {
87                 DBusMessage *reply;
88                 reply =  g_dbus_create_error(message,
89                                                 ERROR_INTERFACE ".Failed",
90                                                 "%s", err->message);
91                 g_error_free(err);
92                 return reply;
93         }
94
95         dbus_message_ref(message);
96
97         return NULL;
98 }
99
100 static void xml_element(GMarkupParseContext *ctxt,
101                         const gchar *element,
102                         const gchar **names,
103                         const gchar **values,
104                         gpointer user_data,
105                         GError **gerr)
106 {
107         DBusMessageIter dict, *iter = user_data;
108         gchar *key;
109         gint i;
110
111         if (strcasecmp("folder", element) != 0 && strcasecmp("file", element) != 0)
112                 return;
113
114         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
115                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
116                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
117                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
118
119         obex_dbus_dict_append(&dict, "Type", DBUS_TYPE_STRING, &element);
120
121         /* FIXME: User, Group, Other permission must be reviewed */
122
123         i = 0;
124         for (key = (gchar *) names[i]; key; key = (gchar *) names[++i]) {
125                 key[0] = g_ascii_toupper(key[0]);
126                 if (g_str_equal("Size", key) == TRUE) {
127                         guint64 size;
128                         size = g_ascii_strtoll(values[i], NULL, 10);
129                         obex_dbus_dict_append(&dict, key, DBUS_TYPE_UINT64,
130                                                                 &size);
131                 } else
132                         obex_dbus_dict_append(&dict, key, DBUS_TYPE_STRING,
133                                                                 &values[i]);
134         }
135
136         dbus_message_iter_close_container(iter, &dict);
137 }
138
139 static const GMarkupParser parser = {
140         xml_element,
141         NULL,
142         NULL,
143         NULL,
144         NULL
145 };
146
147 static void list_folder_callback(struct obc_session *session,
148                                                 struct obc_transfer *transfer,
149                                                 GError *err, void *user_data)
150 {
151         DBusMessage *msg = user_data;
152         GMarkupParseContext *ctxt;
153         DBusMessage *reply;
154         DBusMessageIter iter, array;
155         char *contents;
156         size_t size;
157
158         reply = dbus_message_new_method_return(msg);
159
160         if (obc_transfer_get_contents(transfer, &contents, &size) < 0)
161                 goto done;
162
163         dbus_message_iter_init_append(reply, &iter);
164         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
165                         DBUS_TYPE_ARRAY_AS_STRING
166                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
167                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
168                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
169         ctxt = g_markup_parse_context_new(&parser, 0, &array, NULL);
170         g_markup_parse_context_parse(ctxt, contents, size, NULL);
171         g_markup_parse_context_free(ctxt);
172         dbus_message_iter_close_container(&iter, &array);
173         g_free(contents);
174
175 done:
176         g_dbus_send_message(conn, reply);
177         dbus_message_unref(msg);
178 }
179
180 static DBusMessage *create_folder(DBusConnection *connection,
181                                 DBusMessage *message, void *user_data)
182 {
183         struct ftp_data *ftp = user_data;
184         struct obc_session *session = ftp->session;
185         const char *folder;
186         GError *err = NULL;
187
188         if (dbus_message_get_args(message, NULL,
189                                 DBUS_TYPE_STRING, &folder,
190                                 DBUS_TYPE_INVALID) == FALSE)
191                 return g_dbus_create_error(message,
192                                 ERROR_INTERFACE ".InvalidArguments", NULL);
193
194         obc_session_mkdir(session, folder, async_cb, message, &err);
195         if (err != NULL) {
196                 DBusMessage *reply;
197                 reply = g_dbus_create_error(message,
198                                 ERROR_INTERFACE ".Failed",
199                                 "%s", err->message);
200                 g_error_free(err);
201                 return reply;
202         }
203
204         dbus_message_ref(message);
205
206         return NULL;
207 }
208
209 static DBusMessage *list_folder(DBusConnection *connection,
210                                 DBusMessage *message, void *user_data)
211 {
212         struct ftp_data *ftp = user_data;
213         struct obc_session *session = ftp->session;
214         struct obc_transfer *transfer;
215         GError *err = NULL;
216         DBusMessage *reply;
217
218         transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err);
219         if (transfer == NULL)
220                 goto fail;
221
222         if (obc_session_queue(session, transfer, list_folder_callback,
223                                                         message, &err)) {
224                 dbus_message_ref(message);
225                 return NULL;
226         }
227
228 fail:
229         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
230                                                                 err->message);
231         g_error_free(err);
232         return reply;
233 }
234
235 static DBusMessage *get_file(DBusConnection *connection,
236                                 DBusMessage *message, void *user_data)
237 {
238         struct ftp_data *ftp = user_data;
239         struct obc_session *session = ftp->session;
240         struct obc_transfer *transfer;
241         const char *target_file, *source_file;
242         GError *err = NULL;
243         DBusMessage *reply;
244
245         if (dbus_message_get_args(message, NULL,
246                                 DBUS_TYPE_STRING, &target_file,
247                                 DBUS_TYPE_STRING, &source_file,
248                                 DBUS_TYPE_INVALID) == FALSE)
249                 return g_dbus_create_error(message,
250                                 ERROR_INTERFACE ".InvalidArguments", NULL);
251
252         transfer = obc_transfer_get(NULL, source_file, target_file, &err);
253         if (transfer == NULL)
254                 goto fail;
255
256         if (!obc_session_queue(session, transfer, NULL, NULL, &err))
257                 goto fail;
258
259         return obc_transfer_create_dbus_reply(transfer, message);
260
261 fail:
262         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
263                                                                 err->message);
264         g_error_free(err);
265         return reply;
266 }
267
268 static DBusMessage *put_file(DBusConnection *connection,
269                                 DBusMessage *message, void *user_data)
270 {
271         struct ftp_data *ftp = user_data;
272         struct obc_session *session = ftp->session;
273         struct obc_transfer *transfer;
274         gchar *sourcefile, *targetfile;
275         GError *err = NULL;
276         DBusMessage *reply;
277
278         if (dbus_message_get_args(message, NULL,
279                                         DBUS_TYPE_STRING, &sourcefile,
280                                         DBUS_TYPE_STRING, &targetfile,
281                                         DBUS_TYPE_INVALID) == FALSE)
282                 return g_dbus_create_error(message,
283                                 ERROR_INTERFACE ".InvalidArguments",
284                                 "Invalid arguments in method call");
285
286         transfer = obc_transfer_put(NULL, targetfile, sourcefile, NULL, 0,
287                                                                         &err);
288         if (transfer == NULL)
289                 goto fail;
290
291         if (!obc_session_queue(session, transfer, NULL, NULL, &err))
292                 goto fail;
293
294         return obc_transfer_create_dbus_reply(transfer, message);
295
296 fail:
297         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
298                                                                 err->message);
299         g_error_free(err);
300         return reply;
301 }
302
303 static DBusMessage *copy_file(DBusConnection *connection,
304                                 DBusMessage *message, void *user_data)
305 {
306         struct ftp_data *ftp = user_data;
307         struct obc_session *session = ftp->session;
308         const char *filename, *destname;
309         GError *err = NULL;
310
311         if (dbus_message_get_args(message, NULL,
312                                 DBUS_TYPE_STRING, &filename,
313                                 DBUS_TYPE_STRING, &destname,
314                                 DBUS_TYPE_INVALID) == FALSE)
315                 return g_dbus_create_error(message,
316                                 ERROR_INTERFACE ".InvalidArguments", NULL);
317
318         obc_session_copy(session, filename, destname, async_cb, message, &err);
319         if (err != NULL) {
320                 DBusMessage *reply;
321                 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
322                                                         "%s", err->message);
323                 g_error_free(err);
324                 return reply;
325         }
326
327         dbus_message_ref(message);
328
329         return NULL;
330 }
331
332 static DBusMessage *move_file(DBusConnection *connection,
333                                 DBusMessage *message, void *user_data)
334 {
335         struct ftp_data *ftp = user_data;
336         struct obc_session *session = ftp->session;
337         const char *filename, *destname;
338         GError *err = NULL;
339
340         if (dbus_message_get_args(message, NULL,
341                                 DBUS_TYPE_STRING, &filename,
342                                 DBUS_TYPE_STRING, &destname,
343                                 DBUS_TYPE_INVALID) == FALSE)
344                 return g_dbus_create_error(message,
345                                 ERROR_INTERFACE ".InvalidArguments", NULL);
346
347         obc_session_move(session, filename, destname, async_cb, message, &err);
348         if (err != NULL) {
349                 DBusMessage *reply;
350                 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
351                                                         "%s", err->message);
352                 g_error_free(err);
353                 return reply;
354         }
355
356         dbus_message_ref(message);
357
358         return NULL;
359 }
360
361 static DBusMessage *delete(DBusConnection *connection,
362                                 DBusMessage *message, void *user_data)
363 {
364         struct ftp_data *ftp = user_data;
365         struct obc_session *session = ftp->session;
366         const char *file;
367         GError *err = NULL;
368
369         if (dbus_message_get_args(message, NULL,
370                                 DBUS_TYPE_STRING, &file,
371                                 DBUS_TYPE_INVALID) == FALSE)
372                 return g_dbus_create_error(message,
373                                 ERROR_INTERFACE ".InvalidArguments", NULL);
374
375         obc_session_delete(session, file, async_cb, message, &err);
376         if (err != NULL) {
377                 DBusMessage *reply;
378                 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
379                                                         "%s", err->message);
380                 g_error_free(err);
381                 return reply;
382         }
383
384         dbus_message_ref(message);
385
386         return NULL;
387 }
388
389 static const GDBusMethodTable ftp_methods[] = {
390         { GDBUS_ASYNC_METHOD("ChangeFolder",
391                 GDBUS_ARGS({ "folder", "s" }), NULL, change_folder) },
392         { GDBUS_ASYNC_METHOD("CreateFolder",
393                 GDBUS_ARGS({ "folder", "s" }), NULL, create_folder) },
394         { GDBUS_ASYNC_METHOD("ListFolder",
395                 NULL, GDBUS_ARGS({ "folderinfo", "aa{sv}" }), list_folder) },
396         { GDBUS_METHOD("GetFile",
397                 GDBUS_ARGS({ "targetfile", "s" }, { "sourcefile", "s" }),
398                 GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
399                 get_file) },
400         { GDBUS_METHOD("PutFile",
401                 GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }),
402                 GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
403                 put_file) },
404         { GDBUS_ASYNC_METHOD("CopyFile",
405                 GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }), NULL,
406                 copy_file) },
407         { GDBUS_ASYNC_METHOD("MoveFile",
408                 GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }), NULL,
409                 move_file) },
410         { GDBUS_ASYNC_METHOD("Delete",
411                 GDBUS_ARGS({ "file", "s" }), NULL, delete) },
412         { }
413 };
414
415 static void ftp_free(void *data)
416 {
417         struct ftp_data *ftp = data;
418
419         obc_session_unref(ftp->session);
420         g_free(ftp);
421 }
422
423 static int ftp_probe(struct obc_session *session)
424 {
425         struct ftp_data *ftp;
426         const char *path;
427
428         path = obc_session_get_path(session);
429
430         DBG("%s", path);
431
432         ftp = g_try_new0(struct ftp_data, 1);
433         if (!ftp)
434                 return -ENOMEM;
435
436         ftp->session = obc_session_ref(session);
437
438         if (!g_dbus_register_interface(conn, path, FTP_INTERFACE, ftp_methods,
439                                                 NULL, NULL, ftp, ftp_free)) {
440                 ftp_free(ftp);
441                 return -ENOMEM;
442         }
443
444         return 0;
445 }
446
447 static void ftp_remove(struct obc_session *session)
448 {
449         const char *path = obc_session_get_path(session);
450
451         DBG("%s", path);
452
453         g_dbus_unregister_interface(conn, path, FTP_INTERFACE);
454 }
455
456 static struct obc_driver ftp = {
457         .service = "FTP",
458         .uuid = FTP_UUID,
459         .target = OBEX_FTP_UUID,
460         .target_len = OBEX_FTP_UUID_LEN,
461         .probe = ftp_probe,
462         .remove = ftp_remove
463 };
464
465 static struct obc_driver pcsuite = {
466         .service = "PCSUITE",
467         .uuid = PCSUITE_UUID,
468         .target = OBEX_FTP_UUID,
469         .target_len = OBEX_FTP_UUID_LEN,
470         .probe = ftp_probe,
471         .remove = ftp_remove
472 };
473
474 int ftp_init(void)
475 {
476         int err;
477
478         DBG("");
479
480         conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
481         if (!conn)
482                 return -EIO;
483
484         err = obc_driver_register(&ftp);
485         if (err < 0)
486                 goto failed;
487
488         err = obc_driver_register(&pcsuite);
489         if (err < 0) {
490                 obc_driver_unregister(&ftp);
491                 goto failed;
492         }
493
494         return 0;
495
496 failed:
497         dbus_connection_unref(conn);
498         conn = NULL;
499         return err;
500 }
501
502 void ftp_exit(void)
503 {
504         DBG("");
505
506         dbus_connection_unref(conn);
507         conn = NULL;
508
509         obc_driver_unregister(&ftp);
510         obc_driver_unregister(&pcsuite);
511 }