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