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