upload tizen1.0 source
[profile/ivi/obexd.git] / plugins / pcsuite.c
1 /*
2  *
3  *  OBEX Server
4  *
5  *  Copyright (C) 2007-2010  Nokia Corporation
6  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <dirent.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39 #include <inttypes.h>
40
41 #include <glib.h>
42 #include "gdbus.h"
43
44 #include "obexd.h"
45 #include "plugin.h"
46 #include "log.h"
47 #include "obex.h"
48 #include "mimetype.h"
49 #include "service.h"
50 #include "ftp.h"
51
52 #define PCSUITE_CHANNEL 24
53 #define PCSUITE_WHO_SIZE 8
54
55 #define PCSUITE_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>     \
56 <record>                                                                \
57   <attribute id=\"0x0001\">                                             \
58     <sequence>                                                          \
59       <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/>            \
60     </sequence>                                                         \
61   </attribute>                                                          \
62                                                                         \
63   <attribute id=\"0x0004\">                                             \
64     <sequence>                                                          \
65       <sequence>                                                        \
66         <uuid value=\"0x0100\"/>                                        \
67       </sequence>                                                       \
68       <sequence>                                                        \
69         <uuid value=\"0x0003\"/>                                        \
70         <uint8 value=\"%u\" name=\"channel\"/>                          \
71       </sequence>                                                       \
72       <sequence>                                                        \
73         <uuid value=\"0x0008\"/>                                        \
74       </sequence>                                                       \
75     </sequence>                                                         \
76   </attribute>                                                          \
77                                                                         \
78   <attribute id=\"0x0005\">                                             \
79     <sequence>                                                          \
80       <uuid value=\"0x1002\"/>                                          \
81     </sequence>                                                         \
82   </attribute>                                                          \
83                                                                         \
84   <attribute id=\"0x0009\">                                             \
85     <sequence>                                                          \
86       <sequence>                                                        \
87         <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/>          \
88         <uint16 value=\"0x0100\" name=\"version\"/>                     \
89       </sequence>                                                       \
90     </sequence>                                                         \
91   </attribute>                                                          \
92                                                                         \
93   <attribute id=\"0x0100\">                                             \
94     <text value=\"%s\" name=\"name\"/>                                  \
95   </attribute>                                                          \
96 </record>"
97
98 #define BACKUP_BUS_NAME         "com.nokia.backup.plugin"
99 #define BACKUP_PATH             "/com/nokia/backup"
100 #define BACKUP_PLUGIN_INTERFACE "com.nokia.backup.plugin"
101 #define BACKUP_DBUS_TIMEOUT     (1000 * 60 * 15)
102
103 static const uint8_t FTP_TARGET[TARGET_SIZE] = {
104                         0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
105                         0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
106
107 static const uint8_t PCSUITE_WHO[PCSUITE_WHO_SIZE] = {
108                         'P', 'C', ' ', 'S', 'u', 'i', 't', 'e' };
109
110 struct pcsuite_session {
111         struct ftp_session *ftp;
112         char *lock_file;
113         int fd;
114 };
115
116 static void *pcsuite_connect(struct obex_session *os, int *err)
117 {
118         struct pcsuite_session *pcsuite;
119         struct ftp_session *ftp;
120         int fd;
121         char *filename;
122
123         DBG("");
124
125         ftp = ftp_connect(os, err);
126         if (ftp == NULL)
127                 return NULL;
128
129         filename = g_build_filename(g_get_home_dir(), ".pcsuite", NULL);
130
131         fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
132         if (fd < 0 && errno != EEXIST) {
133                 error("open(%s): %s(%d)", filename, strerror(errno), errno);
134                 goto fail;
135         }
136
137         /* Try to remove the file before retrying since it could be
138            that some process left/crash without removing it */
139         if (fd < 0) {
140                 if (remove(filename) < 0) {
141                         error("remove(%s): %s(%d)", filename, strerror(errno),
142                                                                         errno);
143                         goto fail;
144                 }
145
146                 fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
147                 if (fd < 0) {
148                         error("open(%s): %s(%d)", filename, strerror(errno),
149                                                                         errno);
150                         goto fail;
151                 }
152         }
153
154         DBG("%s created", filename);
155
156         pcsuite = g_new0(struct pcsuite_session, 1);
157         pcsuite->ftp = ftp;
158         pcsuite->lock_file = filename;
159         pcsuite->fd = fd;
160
161         DBG("session %p created", pcsuite);
162
163         if (err)
164                 *err = 0;
165
166         return pcsuite;
167
168 fail:
169         if (ftp)
170                 ftp_disconnect(os, ftp);
171         if (err)
172                 *err = -errno;
173
174         g_free(filename);
175
176         return NULL;
177 }
178
179 static int pcsuite_get(struct obex_session *os, void *user_data)
180 {
181         struct pcsuite_session *pcsuite = user_data;
182
183         DBG("%p", pcsuite);
184
185         return ftp_get(os, pcsuite->ftp);
186 }
187
188 static int pcsuite_chkput(struct obex_session *os, void *user_data)
189 {
190         struct pcsuite_session *pcsuite = user_data;
191
192         DBG("%p", pcsuite);
193
194         return ftp_chkput(os, pcsuite->ftp);
195 }
196
197 static int pcsuite_put(struct obex_session *os, void *user_data)
198 {
199         struct pcsuite_session *pcsuite = user_data;
200
201         DBG("%p", pcsuite);
202
203         return ftp_put(os, pcsuite->ftp);
204 }
205
206 static int pcsuite_setpath(struct obex_session *os, void *user_data)
207 {
208         struct pcsuite_session *pcsuite = user_data;
209
210         DBG("%p", pcsuite);
211
212         return ftp_setpath(os, pcsuite->ftp);
213 }
214
215 static int pcsuite_action(struct obex_session *os, void *user_data)
216 {
217         struct pcsuite_session *pcsuite = user_data;
218
219         DBG("%p", pcsuite);
220
221         return ftp_action(os, pcsuite->ftp);
222 }
223
224 static void pcsuite_disconnect(struct obex_session *os, void *user_data)
225 {
226         struct pcsuite_session *pcsuite = user_data;
227
228         DBG("%p", pcsuite);
229
230         if (pcsuite->fd >= 0)
231                 close(pcsuite->fd);
232
233         if (pcsuite->lock_file) {
234                 remove(pcsuite->lock_file);
235                 g_free(pcsuite->lock_file);
236         }
237
238         if (pcsuite->ftp)
239                 ftp_disconnect(os, pcsuite->ftp);
240
241         g_free(pcsuite);
242 }
243
244 static struct obex_service_driver pcsuite = {
245         .name = "Nokia OBEX PC Suite Services",
246         .service = OBEX_PCSUITE,
247         .channel = PCSUITE_CHANNEL,
248         .secure = TRUE,
249         .record = PCSUITE_RECORD,
250         .target = FTP_TARGET,
251         .target_size = TARGET_SIZE,
252         .who = PCSUITE_WHO,
253         .who_size = PCSUITE_WHO_SIZE,
254         .connect = pcsuite_connect,
255         .get = pcsuite_get,
256         .put = pcsuite_put,
257         .chkput = pcsuite_chkput,
258         .setpath = pcsuite_setpath,
259         .action = pcsuite_action,
260         .disconnect = pcsuite_disconnect
261 };
262
263 struct backup_object {
264         gchar *cmd;
265         int fd;
266         int oflag;
267         int error_code;
268         mode_t mode;
269         DBusPendingCall *pending_call;
270         DBusConnection *conn;
271 };
272
273 static void on_backup_dbus_notify(DBusPendingCall *pending_call,
274                                         void *user_data)
275 {
276         struct backup_object *obj = user_data;
277         DBusMessage *reply;
278         const char *filename;
279         int error_code;
280
281         DBG("Notification received for pending call - %s", obj->cmd);
282
283         reply = dbus_pending_call_steal_reply(pending_call);
284
285         if (reply && dbus_message_get_args(reply, NULL, DBUS_TYPE_INT32,
286                                         &error_code, DBUS_TYPE_STRING,
287                                         &filename, DBUS_TYPE_INVALID)) {
288
289                 obj->error_code = error_code;
290
291                 if (filename) {
292                         DBG("Notification - file path = %s, error_code = %d",
293                                         filename, error_code);
294                         if (error_code == 0)
295                                 obj->fd = open(filename,obj->oflag,obj->mode);
296                 }
297
298         } else
299                 DBG("Notification timed out or connection got closed");
300
301         if (reply)
302                 dbus_message_unref(reply);
303
304         dbus_pending_call_unref(pending_call);
305         obj->pending_call = NULL;
306         dbus_connection_unref(obj->conn);
307         obj->conn = NULL;
308
309         if (obj->fd >= 0) {
310                 DBG("File opened, setting io flags, cmd = %s",
311                                 obj->cmd);
312                 if (obj->oflag == O_RDONLY)
313                         obex_object_set_io_flags(user_data, G_IO_IN, 0);
314                 else
315                         obex_object_set_io_flags(user_data, G_IO_OUT, 0);
316         } else {
317                 DBG("File open error, setting io error, cmd = %s",
318                                 obj->cmd);
319                 obex_object_set_io_flags(user_data, G_IO_ERR, -EPERM);
320         }
321 }
322
323 static gboolean send_backup_dbus_message(const char *oper,
324                                         struct backup_object *obj,
325                                         size_t *size)
326 {
327         DBusConnection *conn;
328         DBusMessage *msg;
329         DBusPendingCall *pending_call;
330         gboolean ret = FALSE;
331         dbus_uint32_t file_size;
332
333         file_size = size ? *size : 0;
334
335         conn = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
336
337         if (conn == NULL)
338                 return FALSE;
339
340         msg = dbus_message_new_method_call(BACKUP_BUS_NAME, BACKUP_PATH,
341                                                 BACKUP_PLUGIN_INTERFACE,
342                                                 "request");
343         if (msg == NULL) {
344                 dbus_connection_unref(conn);
345                 return FALSE;
346         }
347
348         dbus_message_append_args(msg, DBUS_TYPE_STRING, &oper,
349                                         DBUS_TYPE_STRING, &obj->cmd,
350                                         DBUS_TYPE_INT32, &file_size,
351                                         DBUS_TYPE_INVALID);
352
353         if (strcmp(oper, "open") == 0) {
354                 ret = dbus_connection_send_with_reply(conn, msg, &pending_call,
355                                                         BACKUP_DBUS_TIMEOUT);
356                 dbus_message_unref(msg);
357                 if (ret) {
358                         obj->conn = conn;
359                         obj->pending_call = pending_call;
360                         ret = dbus_pending_call_set_notify(pending_call,
361                                                         on_backup_dbus_notify,
362                                                         obj, NULL);
363                 } else
364                         dbus_connection_unref(conn);
365         } else {
366                 ret = dbus_connection_send(conn, msg, NULL);
367                 dbus_message_unref(msg);
368                 dbus_connection_unref(conn);
369         }
370
371         return ret;
372 }
373
374 static void *backup_open(const char *name, int oflag, mode_t mode,
375                                 void *context, size_t *size, int *err)
376 {
377         struct backup_object *obj = g_new0(struct backup_object, 1);
378
379         DBG("cmd = %s", name);
380
381         obj->cmd = g_path_get_basename(name);
382         obj->oflag = oflag;
383         obj->mode = mode;
384         obj->fd = -1;
385         obj->pending_call = NULL;
386         obj->conn = NULL;
387         obj->error_code = 0;
388
389         if (send_backup_dbus_message("open", obj, size) == FALSE) {
390                 g_free(obj);
391                 obj = NULL;
392         }
393
394         if (err)
395                 *err = 0;
396
397         return obj;
398 }
399
400 static int backup_close(void *object)
401 {
402         struct backup_object *obj = object;
403         size_t size = 0;
404
405         DBG("cmd = %s", obj->cmd);
406
407         if (obj->fd != -1)
408                 close(obj->fd);
409
410         if (obj->pending_call) {
411                 dbus_pending_call_cancel(obj->pending_call);
412                 dbus_pending_call_unref(obj->pending_call);
413                 dbus_connection_unref(obj->conn);
414         }
415
416         send_backup_dbus_message("close", obj, &size);
417
418         g_free(obj->cmd);
419         g_free(obj);
420
421         return 0;
422 }
423
424 static ssize_t backup_read(void *object, void *buf, size_t count)
425 {
426         struct backup_object *obj = object;
427         ssize_t ret = 0;
428
429         if (obj->pending_call) {
430                 DBG("cmd = %s, IN WAITING STAGE", obj->cmd);
431                 return -EAGAIN;
432         }
433
434         if (obj->fd != -1) {
435                 DBG("cmd = %s, READING DATA", obj->cmd);
436                 ret = read(obj->fd, buf, count);
437                 if (ret < 0)
438                         ret = -errno;
439         } else {
440                 DBG("cmd = %s, PERMANENT FAILURE", obj->cmd);
441                 ret = obj->error_code ? -obj->error_code : -ENOENT;
442         }
443
444         return ret;
445 }
446
447 static ssize_t backup_write(void *object, const void *buf, size_t count)
448 {
449         struct backup_object *obj = object;
450         ssize_t ret = 0;
451
452         if (obj->pending_call) {
453                 DBG("cmd = %s, IN WAITING STAGE", obj->cmd);
454                 return -EAGAIN;
455         }
456
457         if (obj->fd != -1) {
458                 ret = write(obj->fd, buf, count);
459
460                 DBG("cmd = %s, WRITTING", obj->cmd);
461
462                 if (ret < 0) {
463                         error("backup: cmd = %s", obj->cmd);
464                         ret = -errno;
465                 }
466         } else {
467                 error("backup: cmd = %s", obj->cmd);
468                 ret = obj->error_code ? -obj->error_code : -ENOENT;
469         }
470
471         return ret;
472 }
473
474 static int backup_flush(void *object)
475 {
476         DBG("%p", object);
477
478         return 0;
479 }
480
481 static struct obex_mime_type_driver backup = {
482         .target = FTP_TARGET,
483         .target_size = TARGET_SIZE,
484         .mimetype = "application/vnd.nokia-backup",
485         .open = backup_open,
486         .close = backup_close,
487         .read = backup_read,
488         .write = backup_write,
489         .flush = backup_flush,
490 };
491
492 static int pcsuite_init(void)
493 {
494         int err;
495
496         err = obex_service_driver_register(&pcsuite);
497         if (err < 0)
498                 return err;
499
500         err = obex_mime_type_driver_register(&backup);
501         if (err < 0)
502                 obex_service_driver_unregister(&pcsuite);
503
504         return err;
505 }
506
507 static void pcsuite_exit(void)
508 {
509         obex_mime_type_driver_unregister(&backup);
510         obex_service_driver_unregister(&pcsuite);
511 }
512
513 OBEX_PLUGIN_DEFINE(pcsuite, pcsuite_init, pcsuite_exit)