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