Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / obexd / src / obex.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 <stdio.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <time.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/statvfs.h>
39 #include <fcntl.h>
40 #include <inttypes.h>
41
42 #include <glib.h>
43
44 #include "gobex/gobex.h"
45
46 #include "btio/btio.h"
47 #include "obexd.h"
48 #include "log.h"
49 #include "obex.h"
50 #include "obex-priv.h"
51 #include "server.h"
52 #include "manager.h"
53 #include "mimetype.h"
54 #include "service.h"
55 #include "transport.h"
56
57 static GSList *sessions = NULL;
58
59 typedef struct {
60         uint8_t  version;
61         uint8_t  flags;
62         uint16_t mtu;
63 } __attribute__ ((packed)) obex_connect_hdr_t;
64
65 struct auth_header {
66         uint8_t tag;
67         uint8_t len;
68         uint8_t val[0];
69 } __attribute__ ((packed));
70
71 /* Possible commands */
72 static struct {
73         int cmd;
74         const char *name;
75 } obex_command[] = {
76         { G_OBEX_OP_CONNECT,    "CONNECT"       },
77         { G_OBEX_OP_DISCONNECT, "DISCONNECT"    },
78         { G_OBEX_OP_PUT,        "PUT"           },
79         { G_OBEX_OP_GET,        "GET"           },
80         { G_OBEX_OP_SETPATH,    "SETPATH"       },
81         { G_OBEX_OP_SESSION,    "SESSION"       },
82         { G_OBEX_OP_ABORT,      "ABORT"         },
83         { G_OBEX_OP_ACTION,     "ACTION"        },
84         { 0xFF,                 NULL            },
85 };
86
87 /* Possible Response */
88 static struct {
89         int rsp;
90         const char *name;
91 } obex_response[] = {
92         { G_OBEX_RSP_CONTINUE,                  "CONTINUE"              },
93         { G_OBEX_RSP_SUCCESS,                   "SUCCESS"               },
94         { G_OBEX_RSP_CREATED,                   "CREATED"               },
95         { G_OBEX_RSP_ACCEPTED,                  "ACCEPTED"              },
96         { G_OBEX_RSP_NON_AUTHORITATIVE,         "NON_AUTHORITATIVE"     },
97         { G_OBEX_RSP_NO_CONTENT,                "NO_CONTENT"            },
98         { G_OBEX_RSP_RESET_CONTENT,             "RESET_CONTENT"         },
99         { G_OBEX_RSP_PARTIAL_CONTENT,           "PARTIAL_CONTENT"       },
100         { G_OBEX_RSP_MULTIPLE_CHOICES,          "MULTIPLE_CHOICES"      },
101         { G_OBEX_RSP_MOVED_PERMANENTLY,         "MOVED_PERMANENTLY"     },
102         { G_OBEX_RSP_MOVED_TEMPORARILY,         "MOVED_TEMPORARILY"     },
103         { G_OBEX_RSP_SEE_OTHER,                 "SEE_OTHER"             },
104         { G_OBEX_RSP_NOT_MODIFIED,              "NOT_MODIFIED"          },
105         { G_OBEX_RSP_USE_PROXY,                 "USE_PROXY"             },
106         { G_OBEX_RSP_BAD_REQUEST,               "BAD_REQUEST"           },
107         { G_OBEX_RSP_UNAUTHORIZED,              "UNAUTHORIZED"          },
108         { G_OBEX_RSP_PAYMENT_REQUIRED,          "PAYMENT_REQUIRED"      },
109         { G_OBEX_RSP_FORBIDDEN,                 "FORBIDDEN"             },
110         { G_OBEX_RSP_NOT_FOUND,                 "NOT_FOUND"             },
111         { G_OBEX_RSP_METHOD_NOT_ALLOWED,        "METHOD_NOT_ALLOWED"    },
112         { G_OBEX_RSP_NOT_ACCEPTABLE,            "NOT_ACCEPTABLE"        },
113         { G_OBEX_RSP_PROXY_AUTH_REQUIRED,       "PROXY_AUTH_REQUIRED"   },
114         { G_OBEX_RSP_REQUEST_TIME_OUT,          "REQUEST_TIME_OUT"      },
115         { G_OBEX_RSP_CONFLICT,                  "CONFLICT"              },
116         { G_OBEX_RSP_GONE,                      "GONE"                  },
117         { G_OBEX_RSP_LENGTH_REQUIRED,           "LENGTH_REQUIRED"       },
118         { G_OBEX_RSP_PRECONDITION_FAILED,       "PRECONDITION_FAILED"   },
119         { G_OBEX_RSP_REQ_ENTITY_TOO_LARGE,      "REQ_ENTITY_TOO_LARGE"  },
120         { G_OBEX_RSP_REQ_URL_TOO_LARGE,         "REQ_URL_TOO_LARGE"     },
121         { G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE,    "UNSUPPORTED_MEDIA_TYPE"},
122         { G_OBEX_RSP_INTERNAL_SERVER_ERROR,     "INTERNAL_SERVER_ERROR" },
123         { G_OBEX_RSP_NOT_IMPLEMENTED,           "NOT_IMPLEMENTED"       },
124         { G_OBEX_RSP_BAD_GATEWAY,               "BAD_GATEWAY"           },
125         { G_OBEX_RSP_SERVICE_UNAVAILABLE,       "SERVICE_UNAVAILABLE"   },
126         { G_OBEX_RSP_GATEWAY_TIMEOUT,           "GATEWAY_TIMEOUT"       },
127         { G_OBEX_RSP_VERSION_NOT_SUPPORTED,     "VERSION_NOT_SUPPORTED" },
128         { G_OBEX_RSP_DATABASE_FULL,             "DATABASE_FULL"         },
129         { G_OBEX_RSP_DATABASE_LOCKED,           "DATABASE_LOCKED"       },
130         { 0xFF,                                 NULL                    },
131 };
132
133 static gboolean handle_async_io(void *object, int flags, int err,
134                                                 void *user_data);
135
136 static void print_event(int cmd, int rsp)
137 {
138         const char *cmdstr = NULL, *rspstr = NULL;
139         int i;
140         static int lastcmd;
141
142         if (cmd < 0)
143                 cmd = lastcmd;
144         else
145                 lastcmd = cmd;
146
147         for (i = 0; obex_command[i].cmd != 0xFF; i++) {
148                 if (obex_command[i].cmd != cmd)
149                         continue;
150                 cmdstr = obex_command[i].name;
151         }
152
153         for (i = 0; obex_response[i].rsp != 0xFF; i++) {
154                 if (obex_response[i].rsp != rsp)
155                         continue;
156                 rspstr = obex_response[i].name;
157         }
158
159         obex_debug("%s(0x%x), %s(0x%x)", cmdstr, cmd, rspstr, rsp);
160 }
161
162 static void os_set_response(struct obex_session *os, int err)
163 {
164         uint8_t rsp;
165
166         rsp = g_obex_errno_to_rsp(err);
167
168         print_event(-1, rsp);
169
170         g_obex_send_rsp(os->obex, rsp, NULL, G_OBEX_HDR_INVALID);
171 }
172
173 static void os_session_mark_aborted(struct obex_session *os)
174 {
175         /* the session was already cancelled/aborted or size in unknown */
176         if (os->aborted || os->size == OBJECT_SIZE_UNKNOWN)
177                 return;
178
179         os->aborted = (os->size != os->offset);
180 }
181
182 static void os_reset_session(struct obex_session *os)
183 {
184         os_session_mark_aborted(os);
185
186         if (os->object) {
187                 os->driver->set_io_watch(os->object, NULL, NULL);
188                 os->driver->close(os->object);
189                 if (os->aborted && os->cmd == G_OBEX_OP_PUT && os->path &&
190                                 os->driver->remove)
191                         os->driver->remove(os->path);
192         }
193
194         if (os->service && os->service->reset)
195                 os->service->reset(os, os->service_data);
196
197         if (os->name) {
198                 g_free(os->name);
199                 os->name = NULL;
200         }
201         if (os->type) {
202                 g_free(os->type);
203                 os->type = NULL;
204         }
205         if (os->buf) {
206                 g_free(os->buf);
207                 os->buf = NULL;
208         }
209         if (os->path) {
210                 g_free(os->path);
211                 os->path = NULL;
212         }
213         if (os->apparam) {
214                 g_free(os->apparam);
215                 os->apparam = NULL;
216                 os->apparam_len = 0;
217         }
218
219         if (os->get_rsp > 0) {
220                 g_obex_remove_request_function(os->obex, os->get_rsp);
221                 os->get_rsp = 0;
222         }
223
224         os->object = NULL;
225         os->driver = NULL;
226         os->aborted = FALSE;
227         os->pending = 0;
228         os->offset = 0;
229         os->size = OBJECT_SIZE_DELETE;
230         os->headers_sent = FALSE;
231         os->checked = FALSE;
232 }
233
234 static void obex_session_free(struct obex_session *os)
235 {
236         sessions = g_slist_remove(sessions, os);
237
238         if (os->io)
239                 g_io_channel_unref(os->io);
240
241         if (os->obex)
242                 g_obex_unref(os->obex);
243
244         g_free(os->src);
245         g_free(os->dst);
246
247         g_free(os);
248 }
249
250 /* From Imendio's GnomeVFS OBEX module (om-utils.c) */
251 static time_t parse_iso8610(const char *val, int size)
252 {
253         time_t time, tz_offset = 0;
254         struct tm tm;
255         char *date;
256         char tz;
257         int nr;
258
259         memset(&tm, 0, sizeof(tm));
260         /* According to spec the time doesn't have to be null terminated */
261         date = g_strndup(val, size);
262         nr = sscanf(date, "%04u%02u%02uT%02u%02u%02u%c",
263                         &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
264                         &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
265                         &tz);
266         g_free(date);
267         if (nr < 6) {
268                 /* Invalid time format */
269                 return -1;
270         }
271
272         tm.tm_year -= 1900;     /* Year since 1900 */
273         tm.tm_mon--;            /* Months since January, values 0-11 */
274         tm.tm_isdst = -1;       /* Daylight savings information not avail */
275
276 #if defined(HAVE_TM_GMTOFF)
277         tz_offset = tm.tm_gmtoff;
278 #elif defined(HAVE_TIMEZONE)
279         tz_offset = -timezone;
280         if (tm.tm_isdst > 0)
281                 tz_offset += 3600;
282 #endif
283
284         time = mktime(&tm);
285         if (nr == 7) {
286                 /*
287                  * Date/Time was in localtime (to remote device)
288                  * already. Since we don't know anything about the
289                  * timezone on that one we won't try to apply UTC offset
290                  */
291                 time += tz_offset;
292         }
293
294         return time;
295 }
296
297 static void parse_service(struct obex_session *os, GObexPacket *req)
298 {
299         GObexHeader *hdr;
300         const guint8 *target = NULL, *who = NULL;
301         gsize target_size = 0, who_size = 0;
302
303         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_WHO);
304         if (hdr == NULL)
305                 goto target;
306
307         g_obex_header_get_bytes(hdr, &who, &who_size);
308
309 target:
310         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TARGET);
311         if (hdr == NULL)
312                 goto probe;
313
314         g_obex_header_get_bytes(hdr, &target, &target_size);
315
316 probe:
317         os->service = obex_service_driver_find(os->server->drivers,
318                                                 target, target_size,
319                                                 who, who_size);
320 }
321
322 static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data)
323 {
324         struct obex_session *os = user_data;
325         GObexPacket *rsp;
326         GObexHeader *hdr;
327         int err;
328
329         DBG("");
330
331         print_event(G_OBEX_OP_CONNECT, -1);
332
333         parse_service(os, req);
334
335         if (os->service == NULL || os->service->connect == NULL) {
336                 error("Connect attempt to a non-supported target");
337                 os_set_response(os, -EPERM);
338                 return;
339         }
340
341         DBG("Selected driver: %s", os->service->name);
342
343         os->service_data = os->service->connect(os, &err);
344         if (err < 0) {
345                 os_set_response(os, err);
346                 return;
347         }
348
349         os->cmd = G_OBEX_OP_CONNECT;
350
351         rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID);
352
353         if (os->service->target) {
354                 hdr = g_obex_header_new_bytes(G_OBEX_HDR_WHO,
355                                                 os->service->target,
356                                                 os->service->target_size);
357                 g_obex_packet_add_header(rsp, hdr);
358         }
359
360         g_obex_send(obex, rsp, NULL);
361
362         print_event(-1, 0);
363 }
364
365 static void cmd_disconnect(GObex *obex, GObexPacket *req, void *user_data)
366 {
367         struct obex_session *os = user_data;
368
369         DBG("session %p", os);
370
371         print_event(G_OBEX_OP_DISCONNECT, -1);
372
373         os->cmd = G_OBEX_OP_DISCONNECT;
374
375         os_set_response(os, 0);
376 }
377
378 static ssize_t driver_write(struct obex_session *os)
379 {
380         ssize_t len = 0;
381
382         while (os->pending > 0) {
383                 ssize_t w;
384
385                 w = os->driver->write(os->object, os->buf + len, os->pending);
386                 if (w < 0) {
387                         error("write(): %s (%zd)", strerror(-w), -w);
388                         if (w == -EINTR)
389                                 continue;
390                         else if (w == -EINVAL)
391                                 memmove(os->buf, os->buf + len, os->pending);
392
393                         return w;
394                 }
395
396                 len += w;
397                 os->offset += w;
398                 os->pending -= w;
399         }
400
401         DBG("%zd written", len);
402
403         if (os->service->progress != NULL)
404                 os->service->progress(os, os->service_data);
405
406         return len;
407 }
408
409 static gssize driver_read(struct obex_session *os, void *buf, gsize size)
410 {
411         gssize len;
412
413         if (os->object == NULL)
414                 return -EIO;
415
416         if (os->service->progress != NULL)
417                 os->service->progress(os, os->service_data);
418
419         len = os->driver->read(os->object, buf, size);
420         if (len < 0) {
421                 error("read(): %s (%zd)", strerror(-len), -len);
422                 if (len == -ENOSTR)
423                         return 0;
424                 if (len == -EAGAIN)
425                         os->driver->set_io_watch(os->object, handle_async_io,
426                                                                         os);
427         }
428
429         os->offset += len;
430
431         DBG("%zd read", len);
432
433         return len;
434 }
435
436 static gssize send_data(void *buf, gsize size, gpointer user_data)
437 {
438         struct obex_session *os = user_data;
439
440         DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object,
441                                                                         size);
442
443         if (os->aborted)
444                 return os->err < 0 ? os->err : -EPERM;
445
446         return driver_read(os, buf, size);
447 }
448
449 static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
450 {
451         struct obex_session *os = user_data;
452
453         DBG("");
454
455         if (err != NULL) {
456                 error("transfer failed: %s\n", err->message);
457                 goto reset;
458         }
459
460         if (os->object && os->driver && os->driver->flush) {
461                 if (os->driver->flush(os->object) == -EAGAIN) {
462                         g_obex_suspend(os->obex);
463                         os->driver->set_io_watch(os->object, handle_async_io,
464                                                                         os);
465                         return;
466                 }
467         }
468
469 reset:
470         os_reset_session(os);
471 }
472
473 static int driver_get_headers(struct obex_session *os)
474 {
475         GObexPacket *rsp;
476         gssize len;
477         guint8 data[255];
478         guint8 id;
479         GObexHeader *hdr;
480
481         DBG("name=%s type=%s object=%p", os->name, os->type, os->object);
482
483         if (os->aborted)
484                 return os->err < 0 ? os->err : -EPERM;
485
486         if (os->object == NULL)
487                 return -EIO;
488
489         if (os->headers_sent)
490                 return 0;
491
492         rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID);
493
494         if (os->driver->get_next_header == NULL)
495                 goto done;
496
497         while ((len = os->driver->get_next_header(os->object, &data,
498                                                         sizeof(data), &id))) {
499                 if (len < 0) {
500                         error("get_next_header(): %s (%zd)", strerror(-len),
501                                                                 -len);
502
503                         g_obex_packet_free(rsp);
504
505                         if (len == -EAGAIN)
506                                 return len;
507
508                         g_free(os->buf);
509                         os->buf = NULL;
510
511                         return len;
512                 }
513
514                 hdr = g_obex_header_new_bytes(id, data, len);
515                 g_obex_packet_add_header(rsp, hdr);
516         }
517
518 done:
519         if (os->size != OBJECT_SIZE_UNKNOWN && os->size < UINT32_MAX) {
520                 hdr = g_obex_header_new_uint32(G_OBEX_HDR_LENGTH, os->size);
521                 g_obex_packet_add_header(rsp, hdr);
522         }
523
524         g_obex_get_rsp_pkt(os->obex, rsp, send_data, transfer_complete, os,
525                                                                         NULL);
526
527         os->headers_sent = TRUE;
528
529         print_event(-1, G_OBEX_RSP_CONTINUE);
530
531         return 0;
532 }
533
534 static gboolean handle_async_io(void *object, int flags, int err,
535                                                 void *user_data)
536 {
537         struct obex_session *os = user_data;
538
539         if (err < 0)
540                 goto done;
541
542         if (flags & G_IO_OUT)
543                 err = driver_write(os);
544         if ((flags & G_IO_IN) && !os->headers_sent)
545                 err = driver_get_headers(os);
546
547         if (err == -EAGAIN)
548                 return TRUE;
549
550 done:
551         if (err < 0) {
552                 os->err = err;
553                 os->aborted = TRUE;
554         }
555
556         g_obex_resume(os->obex);
557
558         return FALSE;
559 }
560
561 static gboolean recv_data(const void *buf, gsize size, gpointer user_data)
562 {
563         struct obex_session *os = user_data;
564         ssize_t ret;
565
566         DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object,
567                                                                         size);
568
569         if (os->aborted)
570                 return FALSE;
571
572         /* workaround: client didn't send the object length */
573         if (os->size == OBJECT_SIZE_DELETE)
574                 os->size = OBJECT_SIZE_UNKNOWN;
575
576         os->buf = g_realloc(os->buf, os->pending + size);
577         memcpy(os->buf + os->pending, buf, size);
578         os->pending += size;
579
580         /* only write if both object and driver are valid */
581         if (os->object == NULL || os->driver == NULL) {
582                 DBG("Stored %" PRIu64 " bytes into temporary buffer",
583                                                                 os->pending);
584                 return TRUE;
585         }
586
587         ret = driver_write(os);
588         if (ret >= 0)
589                 return TRUE;
590
591         if (ret == -EAGAIN) {
592                 g_obex_suspend(os->obex);
593                 os->driver->set_io_watch(os->object, handle_async_io, os);
594                 return TRUE;
595         }
596
597         return FALSE;
598 }
599
600 static void parse_type(struct obex_session *os, GObexPacket *req)
601 {
602         GObexHeader *hdr;
603         const guint8 *type;
604         gsize len;
605
606         g_free(os->type);
607         os->type = NULL;
608
609         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TYPE);
610         if (hdr == NULL)
611                 goto probe;
612
613         if (!g_obex_header_get_bytes(hdr, &type, &len))
614                 goto probe;
615
616         /* Ensure null termination */
617         if (type[len - 1] != '\0')
618                 goto probe;
619
620         os->type = g_strndup((const char *) type, len);
621         DBG("TYPE: %s", os->type);
622
623 probe:
624         os->driver = obex_mime_type_driver_find(os->service->target,
625                                                 os->service->target_size,
626                                                 os->type,
627                                                 os->service->who,
628                                                 os->service->who_size);
629 }
630
631 static void parse_name(struct obex_session *os, GObexPacket *req)
632 {
633         GObexHeader *hdr;
634         const char *name;
635
636         g_free(os->name);
637         os->name = NULL;
638
639         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_NAME);
640         if (hdr == NULL)
641                 return;
642
643         if (!g_obex_header_get_unicode(hdr, &name))
644                 return;
645 #ifdef __TIZEN_PATCH__
646         DBG("TYPE===>: %s", os->type);
647         if (name && strcmp(os->type, "x-bt/phonebook")) {
648                 char *new_name;
649                 new_name = strrchr(name, '/');
650                 if (new_name) {
651                         name = new_name + 1;
652                         DBG("FileName %s", name);
653                 }
654         }
655 #endif
656         os->name = g_strdup(name);
657         DBG("NAME: %s", os->name);
658 }
659
660 static void parse_apparam(struct obex_session *os, GObexPacket *req)
661 {
662         GObexHeader *hdr;
663         const guint8 *apparam;
664         gsize len;
665
666         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_APPARAM);
667         if (hdr == NULL)
668                 return;
669
670         if (!g_obex_header_get_bytes(hdr, &apparam, &len))
671                 return;
672
673         os->apparam = g_memdup(apparam, len);
674         os->apparam_len = len;
675         DBG("APPARAM");
676 }
677
678 static void cmd_get(GObex *obex, GObexPacket *req, gpointer user_data)
679 {
680         struct obex_session *os = user_data;
681         int err;
682
683         DBG("session %p", os);
684
685         print_event(G_OBEX_OP_GET, -1);
686
687         if (os->service == NULL) {
688                 os_set_response(os, -EPERM);
689                 return;
690         }
691
692         if (os->service->get == NULL) {
693                 os_set_response(os, -ENOSYS);
694                 return;
695         }
696
697         os->headers_sent = FALSE;
698
699         if (os->type) {
700                 g_free(os->type);
701                 os->type = NULL;
702         }
703
704         parse_type(os, req);
705
706         if (!os->driver) {
707                 error("No driver found");
708                 os_set_response(os, -ENOSYS);
709                 return;
710         }
711
712         os->cmd = G_OBEX_OP_GET;
713
714         parse_name(os, req);
715
716         parse_apparam(os, req);
717
718         err = os->service->get(os, os->service_data);
719         if (err == 0)
720                 return;
721
722         os_set_response(os, err);
723 }
724
725 static void cmd_setpath(GObex *obex, GObexPacket *req, gpointer user_data)
726 {
727         struct obex_session *os = user_data;
728         int err;
729
730         DBG("");
731
732         print_event(G_OBEX_OP_SETPATH, -1);
733
734         if (os->service == NULL) {
735                 err = -EPERM;
736                 goto done;
737         }
738
739         if (os->service->setpath == NULL) {
740                 err = -ENOSYS;
741                 goto done;
742         }
743
744         os->cmd = G_OBEX_OP_SETPATH;
745
746         parse_name(os, req);
747
748         os->nonhdr = g_obex_packet_get_data(req, &os->nonhdr_len);
749
750         err = os->service->setpath(os, os->service_data);
751 done:
752         os_set_response(os, err);
753 }
754
755 int obex_get_stream_start(struct obex_session *os, const char *filename)
756 {
757         int err;
758         void *object;
759         size_t size = OBJECT_SIZE_UNKNOWN;
760
761         object = os->driver->open(filename, O_RDONLY, 0, os->service_data,
762                                                                 &size, &err);
763         if (object == NULL) {
764                 error("open(%s): %s (%d)", filename, strerror(-err), -err);
765                 return err;
766         }
767
768         os->object = object;
769         os->offset = 0;
770         os->size = size;
771
772         err = driver_get_headers(os);
773         if (err != -EAGAIN)
774                 return err;
775
776         g_obex_suspend(os->obex);
777         os->driver->set_io_watch(os->object, handle_async_io, os);
778         return 0;
779 }
780
781 int obex_put_stream_start(struct obex_session *os, const char *filename)
782 {
783         int err;
784
785         os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC,
786 #ifdef __TIZEN_PATCH__
787                                         0644, os->service_data,
788 #else
789                                         0600, os->service_data,
790 #endif
791                                         os->size != OBJECT_SIZE_UNKNOWN ?
792                                         (size_t *) &os->size : NULL, &err);
793         if (os->object == NULL) {
794                 error("open(%s): %s (%d)", filename, strerror(-err), -err);
795                 return err;
796         }
797
798         os->path = g_strdup(filename);
799
800         return 0;
801 }
802
803 static void parse_length(struct obex_session *os, GObexPacket *req)
804 {
805         GObexHeader *hdr;
806         guint32 size;
807
808         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_LENGTH);
809         if (hdr == NULL)
810                 return;
811
812         if (!g_obex_header_get_uint32(hdr, &size))
813                 return;
814
815         os->size = size;
816         DBG("LENGTH: %" PRIu64, os->size);
817 }
818
819 static void parse_time(struct obex_session *os, GObexPacket *req)
820 {
821         GObexHeader *hdr;
822         const guint8 *time;
823         gsize len;
824
825         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TIME);
826         if (hdr == NULL)
827                 return;
828
829
830         if (!g_obex_header_get_bytes(hdr, &time, &len))
831                 return;
832
833         os->time = parse_iso8610((const char *) time, len);
834         DBG("TIME: %s", ctime(&os->time));
835 }
836
837 static gboolean check_put(GObex *obex, GObexPacket *req, void *user_data)
838 {
839         struct obex_session *os = user_data;
840         int ret;
841
842         if (os->service->chkput == NULL)
843                 goto done;
844
845         ret = os->service->chkput(os, os->service_data);
846         switch (ret) {
847         case 0:
848                 break;
849         case -EAGAIN:
850                 g_obex_suspend(os->obex);
851                 os->driver->set_io_watch(os->object, handle_async_io, os);
852                 return TRUE;
853         default:
854                 os_set_response(os, ret);
855                 return FALSE;
856         }
857
858         if (os->size == OBJECT_SIZE_DELETE || os->size == OBJECT_SIZE_UNKNOWN)
859                 DBG("Got a PUT without a Length");
860
861 done:
862         os->checked = TRUE;
863
864         return TRUE;
865 }
866
867 static void cmd_put(GObex *obex, GObexPacket *req, gpointer user_data)
868 {
869         struct obex_session *os = user_data;
870         int err;
871
872         DBG("");
873
874         print_event(G_OBEX_OP_PUT, -1);
875
876         if (os->service == NULL) {
877                 os_set_response(os, -EPERM);
878                 return;
879         }
880
881         parse_type(os, req);
882
883         if (os->driver == NULL) {
884                 error("No driver found");
885                 os_set_response(os, -ENOSYS);
886                 return;
887         }
888
889         os->cmd = G_OBEX_OP_PUT;
890
891         /* Set size to unknown if a body header exists */
892         if (g_obex_packet_get_body(req))
893                 os->size = OBJECT_SIZE_UNKNOWN;
894
895         parse_name(os, req);
896         parse_length(os, req);
897         parse_time(os, req);
898         parse_apparam(os, req);
899
900         if (!os->checked) {
901                 if (!check_put(obex, req, user_data))
902                         return;
903         }
904
905         if (os->service->put == NULL) {
906                 os_set_response(os, -ENOSYS);
907                 return;
908         }
909
910         err = os->service->put(os, os->service_data);
911         if (err == 0) {
912                 g_obex_put_rsp(obex, req, recv_data, transfer_complete, os,
913                                                 NULL, G_OBEX_HDR_INVALID);
914                 print_event(G_OBEX_OP_PUT, G_OBEX_RSP_CONTINUE);
915                 return;
916         }
917
918         os_set_response(os, err);
919 }
920
921 static void parse_destname(struct obex_session *os, GObexPacket *req)
922 {
923         GObexHeader *hdr;
924         const char *destname;
925
926         g_free(os->destname);
927         os->destname = NULL;
928
929         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_DESTNAME);
930         if (hdr == NULL)
931                 return;
932
933         if (!g_obex_header_get_unicode(hdr, &destname))
934                 return;
935
936         os->destname = g_strdup(destname);
937         DBG("DESTNAME: %s", os->destname);
938 }
939
940 static void parse_action(struct obex_session *os, GObexPacket *req)
941 {
942         GObexHeader *hdr;
943         guint8 id;
944
945         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_ACTION);
946         if (hdr == NULL)
947                 return;
948
949         if (!g_obex_header_get_uint8(hdr, &id))
950                 return;
951
952         os->action_id = id;
953         DBG("ACTION: 0x%02x", os->action_id);
954 }
955
956 static void cmd_action(GObex *obex, GObexPacket *req, gpointer user_data)
957 {
958         struct obex_session *os = user_data;
959         int err;
960
961         DBG("");
962
963         print_event(G_OBEX_OP_ACTION, -1);
964
965         if (os->service == NULL) {
966                 err = -EPERM;
967                 goto done;
968         }
969
970         if (os->service->action == NULL) {
971                 err = -ENOSYS;
972                 goto done;
973         }
974
975         os->cmd = G_OBEX_OP_ACTION;
976
977         parse_name(os, req);
978         parse_destname(os, req);
979         parse_action(os, req);
980
981         os->driver = obex_mime_type_driver_find(os->service->target,
982                                                 os->service->target_size,
983                                                 NULL,
984                                                 os->service->who,
985                                                 os->service->who_size);
986         if (os->driver == NULL) {
987                 err = -ENOSYS;
988                 goto done;
989         }
990
991         err = os->service->action(os, os->service_data);
992 done:
993         os_set_response(os, err);
994 }
995
996 static void cmd_abort(GObex *obex, GObexPacket *req, gpointer user_data)
997 {
998         struct obex_session *os = user_data;
999
1000         DBG("");
1001
1002         print_event(G_OBEX_OP_ABORT, -1);
1003
1004         os_reset_session(os);
1005
1006         os_set_response(os, 0);
1007 }
1008
1009 static void obex_session_destroy(struct obex_session *os)
1010 {
1011         DBG("");
1012
1013         os_reset_session(os);
1014
1015         if (os->service && os->service->disconnect)
1016                 os->service->disconnect(os, os->service_data);
1017
1018         obex_session_free(os);
1019 }
1020
1021 static void disconn_func(GObex *obex, GError *err, gpointer user_data)
1022 {
1023         struct obex_session *os = user_data;
1024
1025         error("disconnected: %s\n", err ? err->message : "<no err>");
1026         obex_session_destroy(os);
1027 }
1028
1029 int obex_session_start(GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu,
1030                                 gboolean stream, struct obex_server *server)
1031 {
1032         struct obex_session *os;
1033         GObex *obex;
1034         GObexTransportType type;
1035         static uint32_t id = 0;
1036
1037         DBG("");
1038
1039         os = g_new0(struct obex_session, 1);
1040         os->id = ++id;
1041
1042         os->service = obex_service_driver_find(server->drivers, NULL,
1043                                                         0, NULL, 0);
1044         os->server = server;
1045         os->size = OBJECT_SIZE_DELETE;
1046
1047         type = stream ? G_OBEX_TRANSPORT_STREAM : G_OBEX_TRANSPORT_PACKET;
1048
1049         obex = g_obex_new(io, type, rx_mtu, tx_mtu);
1050         if (!obex) {
1051                 obex_session_free(os);
1052                 return -EIO;
1053         }
1054
1055         g_obex_set_disconnect_function(obex, disconn_func, os);
1056         g_obex_add_request_function(obex, G_OBEX_OP_CONNECT, cmd_connect, os);
1057         g_obex_add_request_function(obex, G_OBEX_OP_DISCONNECT, cmd_disconnect,
1058                                                                         os);
1059         g_obex_add_request_function(obex, G_OBEX_OP_PUT, cmd_put, os);
1060         g_obex_add_request_function(obex, G_OBEX_OP_GET, cmd_get, os);
1061         g_obex_add_request_function(obex, G_OBEX_OP_SETPATH, cmd_setpath, os);
1062         g_obex_add_request_function(obex, G_OBEX_OP_ACTION, cmd_action, os);
1063         g_obex_add_request_function(obex, G_OBEX_OP_ABORT, cmd_abort, os);
1064
1065         os->obex = obex;
1066         os->io = g_io_channel_ref(io);
1067
1068         obex_getsockname(os, &os->src);
1069         obex_getpeername(os, &os->dst);
1070
1071         sessions = g_slist_prepend(sessions, os);
1072
1073         return 0;
1074 }
1075
1076 const char *obex_get_name(struct obex_session *os)
1077 {
1078         return os->name;
1079 }
1080
1081 const char *obex_get_destname(struct obex_session *os)
1082 {
1083         return os->destname;
1084 }
1085
1086 void obex_set_name(struct obex_session *os, const char *name)
1087 {
1088         g_free(os->name);
1089         os->name = g_strdup(name);
1090         DBG("Name changed: %s", os->name);
1091 }
1092
1093 ssize_t obex_get_size(struct obex_session *os)
1094 {
1095         return os->size;
1096 }
1097
1098 const char *obex_get_type(struct obex_session *os)
1099 {
1100         return os->type;
1101 }
1102
1103 int obex_remove(struct obex_session *os, const char *path)
1104 {
1105         if (os->driver == NULL)
1106                 return -ENOSYS;
1107
1108         return os->driver->remove(path);
1109 }
1110
1111 int obex_copy(struct obex_session *os, const char *source,
1112                                                 const char *destination)
1113 {
1114         if (os->driver == NULL || os->driver->copy == NULL)
1115                 return -ENOSYS;
1116
1117         DBG("%s %s", source, destination);
1118
1119         return os->driver->copy(source, destination);
1120 }
1121
1122 int obex_move(struct obex_session *os, const char *source,
1123                                                 const char *destination)
1124 {
1125         if (os->driver == NULL || os->driver->move == NULL)
1126                 return -ENOSYS;
1127
1128         DBG("%s %s", source, destination);
1129
1130         return os->driver->move(source, destination);
1131 }
1132
1133 uint8_t obex_get_action_id(struct obex_session *os)
1134 {
1135         return os->action_id;
1136 }
1137
1138 ssize_t obex_get_apparam(struct obex_session *os, const uint8_t **buffer)
1139 {
1140         *buffer = os->apparam;
1141
1142         return os->apparam_len;
1143 }
1144
1145 ssize_t obex_get_non_header_data(struct obex_session *os,
1146                                                         const uint8_t **data)
1147 {
1148         *data = os->nonhdr;
1149
1150         return os->nonhdr_len;
1151 }
1152
1153 int obex_getpeername(struct obex_session *os, char **name)
1154 {
1155         struct obex_transport_driver *transport = os->server->transport;
1156
1157         if (transport == NULL || transport->getpeername == NULL)
1158                 return -ENOTSUP;
1159
1160         return transport->getpeername(os->io, name);
1161 }
1162
1163 int obex_getsockname(struct obex_session *os, char **name)
1164 {
1165         struct obex_transport_driver *transport = os->server->transport;
1166
1167         if (transport == NULL || transport->getsockname == NULL)
1168                 return -ENOTSUP;
1169
1170         return transport->getsockname(os->io, name);
1171 }
1172
1173 int memncmp0(const void *a, size_t na, const void *b, size_t nb)
1174 {
1175         if (na != nb)
1176                 return na - nb;
1177
1178         if (a == NULL)
1179                 return -(a != b);
1180
1181         if (b == NULL)
1182                 return a != b;
1183
1184         return memcmp(a, b, na);
1185 }