1b180599d009f51a22de924e91c9911feb7bb1cf
[platform/upstream/obexd.git] / plugins / mas.c
1 /*
2  *
3  *  OBEX Server
4  *
5  *  Copyright (C) 2010-2011  Nokia Corporation
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <errno.h>
30 #include <glib.h>
31 #include <fcntl.h>
32 #include <inttypes.h>
33
34 #include <gobex/gobex.h>
35 #include <gobex/gobex-apparam.h>
36
37 #include "obexd.h"
38 #include "plugin.h"
39 #include "log.h"
40 #include "obex.h"
41 #include "service.h"
42 #include "mimetype.h"
43 #include "filesystem.h"
44 #include "manager.h"
45 #include "map_ap.h"
46
47 #include "messages.h"
48
49 #define READ_STATUS_REQ 0
50 #define DELETE_STATUS_REQ 1
51
52 /* Channel number according to bluez doc/assigned-numbers.txt */
53 #define MAS_CHANNEL     16
54
55 #define MAS_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>         \
56 <record>                                                                \
57   <attribute id=\"0x0001\">                                             \
58     <sequence>                                                          \
59       <uuid value=\"0x1132\"/>                                          \
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=\"0x0009\">                                             \
79     <sequence>                                                          \
80       <sequence>                                                        \
81         <uuid value=\"0x1134\"/>                                        \
82         <uint16 value=\"0x0100\" name=\"version\"/>                     \
83       </sequence>                                                       \
84     </sequence>                                                         \
85   </attribute>                                                          \
86                                                                         \
87   <attribute id=\"0x0100\">                                             \
88     <text value=\"%s\" name=\"name\"/>                                  \
89   </attribute>                                                          \
90                                                                         \
91   <attribute id=\"0x0315\">                                             \
92     <uint8 value=\"0x00\"/>                                             \
93   </attribute>                                                          \
94                                                                         \
95   <attribute id=\"0x0316\">                                             \
96     <uint8 value=\"0x0F\"/>                                             \
97   </attribute>                                                          \
98 </record>"
99
100 #define XML_DECL "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
101
102 /* Building blocks for x-obex/folder-listing */
103 #define FL_DTD "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">"
104 #define FL_BODY_BEGIN "<folder-listing version=\"1.0\">"
105 #define FL_BODY_EMPTY "<folder-listing version=\"1.0\"/>"
106 #define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>"
107 #define FL_FOLDER_ELEMENT "<folder name=\"%s\"/>"
108 #define FL_BODY_END "</folder-listing>"
109
110 #define ML_BODY_BEGIN "<MAP-msg-listing version=\"1.0\">"
111 #define ML_BODY_END "</MAP-msg-listing>"
112
113 struct mas_session {
114         struct mas_request *request;
115         void *backend_data;
116         gboolean finished;
117         gboolean nth_call;
118         GString *buffer;
119         GObexApparam *inparams;
120         GObexApparam *outparams;
121         gboolean ap_sent;
122 };
123
124 static const uint8_t MAS_TARGET[TARGET_SIZE] = {
125                         0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, 0xdb,
126                         0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66  };
127
128 static int get_params(struct obex_session *os, struct mas_session *mas)
129 {
130         const uint8_t *buffer;
131         ssize_t size;
132
133         size = obex_get_apparam(os, &buffer);
134         if (size < 0)
135                 size = 0;
136
137         mas->inparams = g_obex_apparam_decode(buffer, size);
138         if (mas->inparams == NULL) {
139                 DBG("Error when parsing parameters!");
140                 return -EBADR;
141         }
142
143         return 0;
144 }
145
146 static void reset_request(struct mas_session *mas)
147 {
148         if (mas->buffer) {
149                 g_string_free(mas->buffer, TRUE);
150                 mas->buffer = NULL;
151         }
152
153         if (mas->inparams) {
154                 g_obex_apparam_free(mas->inparams);
155                 mas->inparams = NULL;
156         }
157
158         if (mas->outparams) {
159                 g_obex_apparam_free(mas->outparams);
160                 mas->outparams = NULL;
161         }
162
163         mas->nth_call = FALSE;
164         mas->finished = FALSE;
165         mas->ap_sent = FALSE;
166 }
167
168 static void mas_clean(struct mas_session *mas)
169 {
170         reset_request(mas);
171         g_free(mas);
172 }
173
174 static void *mas_connect(struct obex_session *os, int *err)
175 {
176         struct mas_session *mas;
177
178         DBG("");
179
180         mas = g_new0(struct mas_session, 1);
181
182         *err = messages_connect(&mas->backend_data);
183         if (*err < 0)
184                 goto failed;
185
186         manager_register_session(os);
187
188         return mas;
189
190 failed:
191         g_free(mas);
192
193         return NULL;
194 }
195
196 static void mas_disconnect(struct obex_session *os, void *user_data)
197 {
198         struct mas_session *mas = user_data;
199
200         DBG("");
201
202         manager_unregister_session(os);
203         messages_disconnect(mas->backend_data);
204
205         mas_clean(mas);
206 }
207
208 static int mas_get(struct obex_session *os, void *user_data)
209 {
210         struct mas_session *mas = user_data;
211         const char *type = obex_get_type(os);
212         const char *name = obex_get_name(os);
213         int ret;
214
215         DBG("GET: name %s type %s mas %p",
216                         name, type, mas);
217
218         if (type == NULL)
219                 return -EBADR;
220
221         ret = get_params(os, mas);
222         if (ret < 0)
223                 goto failed;
224
225         ret = obex_get_stream_start(os, name);
226         if (ret < 0)
227                 goto failed;
228
229         return 0;
230
231 failed:
232         reset_request(mas);
233
234         return ret;
235 }
236
237 static int mas_put(struct obex_session *os, void *user_data)
238 {
239         struct mas_session *mas = user_data;
240         const char *type = obex_get_type(os);
241         const char *name = obex_get_name(os);
242         int ret;
243
244         DBG("PUT: name %s type %s mas %p", name, type, mas);
245
246         if (type == NULL)
247                 return -EBADR;
248
249         ret = get_params(os, mas);
250         if (ret < 0)
251                 goto failed;
252
253         ret = obex_put_stream_start(os, name);
254         if (ret < 0)
255                 goto failed;
256
257         return 0;
258
259 failed:
260         reset_request(mas);
261
262         return ret;
263 }
264
265 /* FIXME: Preserve whitespaces */
266 static void g_string_append_escaped_printf(GString *string,
267                                                 const gchar *format, ...)
268 {
269         va_list ap;
270         char *escaped;
271
272         va_start(ap, format);
273         escaped = g_markup_vprintf_escaped(format, ap);
274         g_string_append(string, escaped);
275         g_free(escaped);
276         va_end(ap);
277 }
278
279 static const char *yesorno(gboolean a)
280 {
281         if (a)
282                 return "yes";
283
284         return "no";
285 }
286
287 static void get_messages_listing_cb(void *session, int err, uint16_t size,
288                                         gboolean newmsg,
289                                         const struct messages_message *entry,
290                                         void *user_data)
291 {
292         struct mas_session *mas = user_data;
293         uint16_t max = 1024;
294
295         if (err < 0 && err != -EAGAIN) {
296                 obex_object_set_io_flags(mas, G_IO_ERR, err);
297                 return;
298         }
299
300         g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
301
302         if (max == 0) {
303                 if (!entry)
304                         mas->finished = TRUE;
305
306                 goto proceed;
307         }
308
309         if (!mas->nth_call) {
310                 g_string_append(mas->buffer, ML_BODY_BEGIN);
311                 mas->nth_call = TRUE;
312         }
313
314         if (!entry) {
315                 g_string_append(mas->buffer, ML_BODY_END);
316                 mas->finished = TRUE;
317
318                 goto proceed;
319         }
320
321         g_string_append(mas->buffer, "<msg");
322
323         g_string_append_escaped_printf(mas->buffer, " handle=\"%s\"",
324                                                                 entry->handle);
325
326         if (entry->mask & PMASK_SUBJECT)
327                 g_string_append_escaped_printf(mas->buffer, " subject=\"%s\"",
328                                 entry->subject);
329
330         if (entry->mask & PMASK_DATETIME)
331                 g_string_append_escaped_printf(mas->buffer, " datetime=\"%s\"",
332                                 entry->datetime);
333
334         if (entry->mask & PMASK_SENDER_NAME)
335                 g_string_append_escaped_printf(mas->buffer,
336                                                 " sender_name=\"%s\"",
337                                                 entry->sender_name);
338
339         if (entry->mask & PMASK_SENDER_ADDRESSING)
340                 g_string_append_escaped_printf(mas->buffer,
341                                                 " sender_addressing=\"%s\"",
342                                                 entry->sender_addressing);
343
344         if (entry->mask & PMASK_REPLYTO_ADDRESSING)
345                 g_string_append_escaped_printf(mas->buffer,
346                                                 " replyto_addressing=\"%s\"",
347                                                 entry->replyto_addressing);
348
349         if (entry->mask & PMASK_RECIPIENT_NAME)
350                 g_string_append_escaped_printf(mas->buffer,
351                                                 " recipient_name=\"%s\"",
352                                                 entry->recipient_name);
353
354         if (entry->mask & PMASK_RECIPIENT_ADDRESSING)
355                 g_string_append_escaped_printf(mas->buffer,
356                                                 " recipient_addressing=\"%s\"",
357                                                 entry->recipient_addressing);
358
359         if (entry->mask & PMASK_TYPE)
360                 g_string_append_escaped_printf(mas->buffer, " type=\"%s\"",
361                                 entry->type);
362
363         if (entry->mask & PMASK_RECEPTION_STATUS)
364                 g_string_append_escaped_printf(mas->buffer,
365                                                 " reception_status=\"%s\"",
366                                                 entry->reception_status);
367
368         if (entry->mask & PMASK_SIZE)
369                 g_string_append_escaped_printf(mas->buffer, " size=\"%s\"",
370                                 entry->size);
371
372         if (entry->mask & PMASK_ATTACHMENT_SIZE)
373                 g_string_append_escaped_printf(mas->buffer,
374                                                 " attachment_size=\"%s\"",
375                                                 entry->attachment_size);
376
377         if (entry->mask & PMASK_TEXT)
378                 g_string_append_escaped_printf(mas->buffer, " text=\"%s\"",
379                                 yesorno(entry->text));
380
381         if (entry->mask & PMASK_READ)
382                 g_string_append_escaped_printf(mas->buffer, " read=\"%s\"",
383                                 yesorno(entry->read));
384
385         if (entry->mask & PMASK_SENT)
386                 g_string_append_escaped_printf(mas->buffer, " sent=\"%s\"",
387                                 yesorno(entry->sent));
388
389         if (entry->mask & PMASK_PROTECTED)
390                 g_string_append_escaped_printf(mas->buffer, " protected=\"%s\"",
391                                 yesorno(entry->protect));
392
393         if (entry->mask & PMASK_PRIORITY)
394                 g_string_append_escaped_printf(mas->buffer, " priority=\"%s\"",
395                                 yesorno(entry->priority));
396
397         g_string_append(mas->buffer, "/>\n");
398
399 proceed:
400         if (!entry) {
401                 mas->outparams = g_obex_apparam_set_uint16(mas->outparams,
402                                                 MAP_AP_MESSAGESLISTINGSIZE,
403                                                 size);
404                 mas->outparams = g_obex_apparam_set_uint8(mas->outparams,
405                                                 MAP_AP_NEWMESSAGE,
406                                                 newmsg ? 1 : 0);
407         }
408
409         if (err != -EAGAIN)
410                 obex_object_set_io_flags(mas, G_IO_IN, 0);
411 }
412
413 static void get_message_cb(void *session, int err, gboolean fmore,
414                                         const char *chunk, void *user_data)
415 {
416         struct mas_session *mas = user_data;
417
418         DBG("");
419
420         if (err < 0 && err != -EAGAIN) {
421                 obex_object_set_io_flags(mas, G_IO_ERR, err);
422                 return;
423         }
424
425         if (!chunk) {
426                 mas->finished = TRUE;
427                 goto proceed;
428         }
429
430         g_string_append(mas->buffer, chunk);
431
432 proceed:
433         if (err != -EAGAIN)
434                 obex_object_set_io_flags(mas, G_IO_IN, 0);
435 }
436
437 static void get_folder_listing_cb(void *session, int err, uint16_t size,
438                                         const char *name, void *user_data)
439 {
440         struct mas_session *mas = user_data;
441         uint16_t max = 1024;
442
443         if (err < 0 && err != -EAGAIN) {
444                 obex_object_set_io_flags(mas, G_IO_ERR, err);
445                 return;
446         }
447
448         g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
449
450         if (max == 0) {
451                 if (err != -EAGAIN)
452                         mas->outparams = g_obex_apparam_set_uint16(
453                                                 mas->outparams,
454                                                 MAP_AP_FOLDERLISTINGSIZE,
455                                                 size);
456
457                 if (!name)
458                         mas->finished = TRUE;
459
460                 goto proceed;
461         }
462
463         if (!mas->nth_call) {
464                 g_string_append(mas->buffer, XML_DECL);
465                 g_string_append(mas->buffer, FL_DTD);
466                 if (!name) {
467                         g_string_append(mas->buffer, FL_BODY_EMPTY);
468                         mas->finished = TRUE;
469                         goto proceed;
470                 }
471                 g_string_append(mas->buffer, FL_BODY_BEGIN);
472                 mas->nth_call = TRUE;
473         }
474
475         if (!name) {
476                 g_string_append(mas->buffer, FL_BODY_END);
477                 mas->finished = TRUE;
478                 goto proceed;
479         }
480
481         if (g_strcmp0(name, "..") == 0)
482                 g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT);
483         else
484                 g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT,
485                                                                         name);
486
487 proceed:
488         if (err != -EAGAIN)
489                 obex_object_set_io_flags(mas, G_IO_IN, err);
490 }
491
492 static void set_status_cb(void *session, int err, void *user_data)
493 {
494         struct mas_session *mas = user_data;
495
496         DBG("");
497
498         mas->finished = TRUE;
499
500         if (err < 0)
501                 obex_object_set_io_flags(mas, G_IO_ERR, err);
502         else
503                 obex_object_set_io_flags(mas, G_IO_OUT, 0);
504 }
505
506 static int mas_setpath(struct obex_session *os, void *user_data)
507 {
508         const char *name;
509         const uint8_t *nonhdr;
510         struct mas_session *mas = user_data;
511
512         if (obex_get_non_header_data(os, &nonhdr) != 2) {
513                 error("Set path failed: flag and constants not found!");
514                 return -EBADR;
515         }
516
517         name = obex_get_name(os);
518
519         DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]);
520
521         if ((nonhdr[0] & 0x02) != 0x02) {
522                 DBG("Error: requested directory creation");
523                 return -EBADR;
524         }
525
526         return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01);
527 }
528
529 static void *folder_listing_open(const char *name, int oflag, mode_t mode,
530                                 void *driver_data, size_t *size, int *err)
531 {
532         struct mas_session *mas = driver_data;
533         /* 1024 is the default when there was no MaxListCount sent */
534         uint16_t max = 1024;
535         uint16_t offset = 0;
536
537         if (oflag != O_RDONLY) {
538                 *err = -EBADR;
539                 return NULL;
540         }
541
542         DBG("name = %s", name);
543
544         g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
545         g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
546
547         *err = messages_get_folder_listing(mas->backend_data, name, max,
548                                         offset, get_folder_listing_cb, mas);
549
550         mas->buffer = g_string_new("");
551
552         if (*err < 0)
553                 return NULL;
554         else
555                 return mas;
556 }
557
558 static void *msg_listing_open(const char *name, int oflag, mode_t mode,
559                                 void *driver_data, size_t *size, int *err)
560 {
561         struct mas_session *mas = driver_data;
562         struct messages_filter filter = { 0, };
563         /* 1024 is the default when there was no MaxListCount sent */
564         uint16_t max = 1024;
565         uint16_t offset = 0;
566         /* If MAP client does not specify the subject length,
567            then subject_len = 0 and subject should be sent unaltered. */
568         uint8_t subject_len = 0;
569
570         DBG("");
571
572         if (oflag != O_RDONLY) {
573                 *err = -EBADR;
574                 return NULL;
575         }
576
577         g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
578         g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
579         g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH,
580                                                 &subject_len);
581
582         g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK,
583                                                 &filter.parameter_mask);
584         g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE,
585                                                 &filter.type);
586         filter.period_begin = g_obex_apparam_get_string(mas->inparams,
587                                                 MAP_AP_FILTERPERIODBEGIN);
588         filter.period_end = g_obex_apparam_get_string(mas->inparams,
589                                                 MAP_AP_FILTERPERIODEND);
590         g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS,
591                                                 &filter.read_status);
592         filter.recipient = g_obex_apparam_get_string(mas->inparams,
593                                                 MAP_AP_FILTERRECIPIENT);
594         filter.originator = g_obex_apparam_get_string(mas->inparams,
595                                                 MAP_AP_FILTERORIGINATOR);
596         g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY,
597                                                 &filter.priority);
598
599         *err = messages_get_messages_listing(mas->backend_data, name, max,
600                         offset, subject_len, &filter,
601                         get_messages_listing_cb, mas);
602
603         mas->buffer = g_string_new("");
604
605         if (*err < 0)
606                 return NULL;
607         else
608                 return mas;
609 }
610
611 static void *message_open(const char *name, int oflag, mode_t mode,
612                                 void *driver_data, size_t *size, int *err)
613 {
614         struct mas_session *mas = driver_data;
615
616         DBG("");
617
618         if (oflag != O_RDONLY) {
619                 DBG("Message pushing unsupported");
620                 *err = -ENOSYS;
621
622                 return NULL;
623         }
624
625         *err = messages_get_message(mas->backend_data, name, 0,
626                         get_message_cb, mas);
627
628         mas->buffer = g_string_new("");
629
630         if (*err < 0)
631                 return NULL;
632         else
633                 return mas;
634 }
635
636 static void *message_update_open(const char *name, int oflag, mode_t mode,
637                                         void *driver_data, size_t *size,
638                                         int *err)
639 {
640         struct mas_session *mas = driver_data;
641
642         DBG("");
643
644         if (oflag == O_RDONLY) {
645                 *err = -EBADR;
646                 return NULL;
647         }
648
649         *err = messages_update_inbox(mas->backend_data, set_status_cb, mas);
650         if (*err < 0)
651                 return NULL;
652         else
653                 return mas;
654 }
655
656 static void *message_set_status_open(const char *name, int oflag, mode_t mode,
657                                         void *driver_data, size_t *size,
658                                         int *err)
659
660 {
661         struct mas_session *mas = driver_data;
662         uint8_t indicator;
663         uint8_t value;
664
665         DBG("");
666
667         if (oflag == O_RDONLY) {
668                 *err = -EBADR;
669                 return NULL;
670         }
671
672         if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR,
673                                                                 &indicator)) {
674                 *err = -EBADR;
675                 return NULL;
676         }
677
678         if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE,
679                                                                 &value)) {
680                 *err = -EBADR;
681                 return NULL;
682         }
683
684         if (indicator == READ_STATUS_REQ)
685                 *err = messages_set_read(mas->backend_data, name, value,
686                                                         set_status_cb, mas);
687         else if (indicator == DELETE_STATUS_REQ)
688                 *err = messages_set_delete(mas->backend_data, name, value,
689                                                         set_status_cb, mas);
690         else
691                 *err = -EBADR;
692
693         if (*err < 0)
694                 return NULL;
695
696         return mas;
697 }
698
699 static ssize_t any_get_next_header(void *object, void *buf, size_t mtu,
700                                                                 uint8_t *hi)
701 {
702         struct mas_session *mas = object;
703
704         DBG("");
705
706         if (mas->buffer->len == 0 && !mas->finished)
707                 return -EAGAIN;
708
709         *hi = G_OBEX_HDR_APPARAM;
710
711         if (mas->ap_sent)
712                 return 0;
713
714         mas->ap_sent = TRUE;
715         return g_obex_apparam_encode(mas->outparams, buf, mtu);
716 }
717
718 static void *any_open(const char *name, int oflag, mode_t mode,
719                                 void *driver_data, size_t *size, int *err)
720 {
721         DBG("");
722
723         *err = -ENOSYS;
724
725         return NULL;
726 }
727
728 static ssize_t any_write(void *object, const void *buf, size_t count)
729 {
730         DBG("");
731
732         return count;
733 }
734
735 static ssize_t any_read(void *obj, void *buf, size_t count)
736 {
737         struct mas_session *mas = obj;
738         ssize_t len;
739
740         DBG("");
741
742         len = string_read(mas->buffer, buf, count);
743
744         if (len == 0 && !mas->finished)
745                 return -EAGAIN;
746
747         return len;
748 }
749
750 static int any_close(void *obj)
751 {
752         struct mas_session *mas = obj;
753
754         DBG("");
755
756         if (!mas->finished)
757                 messages_abort(mas->backend_data);
758
759         reset_request(mas);
760
761         return 0;
762 }
763
764 static struct obex_service_driver mas = {
765         .name = "Message Access server",
766         .service = OBEX_MAS,
767         .channel = MAS_CHANNEL,
768         .secure = TRUE,
769         .record = MAS_RECORD,
770         .target = MAS_TARGET,
771         .target_size = TARGET_SIZE,
772         .connect = mas_connect,
773         .get = mas_get,
774         .put = mas_put,
775         .setpath = mas_setpath,
776         .disconnect = mas_disconnect,
777 };
778
779 static struct obex_mime_type_driver mime_map = {
780         .target = MAS_TARGET,
781         .target_size = TARGET_SIZE,
782         .mimetype = NULL,
783         .open = any_open,
784         .close = any_close,
785         .read = any_read,
786         .write = any_write,
787 };
788
789 static struct obex_mime_type_driver mime_message = {
790         .target = MAS_TARGET,
791         .target_size = TARGET_SIZE,
792         .mimetype = "x-bt/message",
793         .open = message_open,
794         .close = any_close,
795         .read = any_read,
796         .write = any_write,
797 };
798
799 static struct obex_mime_type_driver mime_folder_listing = {
800         .target = MAS_TARGET,
801         .target_size = TARGET_SIZE,
802         .mimetype = "x-obex/folder-listing",
803         .get_next_header = any_get_next_header,
804         .open = folder_listing_open,
805         .close = any_close,
806         .read = any_read,
807         .write = any_write,
808 };
809
810 static struct obex_mime_type_driver mime_msg_listing = {
811         .target = MAS_TARGET,
812         .target_size = TARGET_SIZE,
813         .mimetype = "x-bt/MAP-msg-listing",
814         .get_next_header = any_get_next_header,
815         .open = msg_listing_open,
816         .close = any_close,
817         .read = any_read,
818         .write = any_write,
819 };
820
821 static struct obex_mime_type_driver mime_notification_registration = {
822         .target = MAS_TARGET,
823         .target_size = TARGET_SIZE,
824         .mimetype = "x-bt/MAP-NotificationRegistration",
825         .open = any_open,
826         .close = any_close,
827         .read = any_read,
828         .write = any_write,
829 };
830
831 static struct obex_mime_type_driver mime_message_status = {
832         .target = MAS_TARGET,
833         .target_size = TARGET_SIZE,
834         .mimetype = "x-bt/messageStatus",
835         .open = message_set_status_open,
836         .close = any_close,
837         .read = any_read,
838         .write = any_write,
839 };
840
841 static struct obex_mime_type_driver mime_message_update = {
842         .target = MAS_TARGET,
843         .target_size = TARGET_SIZE,
844         .mimetype = "x-bt/MAP-messageUpdate",
845         .open = message_update_open,
846         .close = any_close,
847         .read = any_read,
848         .write = any_write,
849 };
850
851 static struct obex_mime_type_driver *map_drivers[] = {
852         &mime_map,
853         &mime_message,
854         &mime_folder_listing,
855         &mime_msg_listing,
856         &mime_notification_registration,
857         &mime_message_status,
858         &mime_message_update,
859         NULL
860 };
861
862 static int mas_init(void)
863 {
864         int err;
865         int i;
866
867         err = messages_init();
868         if (err < 0)
869                 return err;
870
871         for (i = 0; map_drivers[i] != NULL; ++i) {
872                 err = obex_mime_type_driver_register(map_drivers[i]);
873                 if (err < 0)
874                         goto failed;
875         }
876
877         err = obex_service_driver_register(&mas);
878         if (err < 0)
879                 goto failed;
880
881         return 0;
882
883 failed:
884         for (--i; i >= 0; --i)
885                 obex_mime_type_driver_unregister(map_drivers[i]);
886
887         messages_exit();
888
889         return err;
890 }
891
892 static void mas_exit(void)
893 {
894         int i;
895
896         obex_service_driver_unregister(&mas);
897
898         for (i = 0; map_drivers[i] != NULL; ++i)
899                 obex_mime_type_driver_unregister(map_drivers[i]);
900
901         messages_exit();
902 }
903
904 OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit)