~awe/phablet-extras/ofono-lp1204683

« back to all changes in this revision

Viewing changes to src/phonebook.c

  • Committer: Bazaar Package Importer
  • Author(s): Andres Salomon
  • Date: 2009-08-15 15:55:11 UTC
  • Revision ID: james.westby@ubuntu.com-20090815155511-frst06dijguhyfi4
Tags: upstream-0.3
ImportĀ upstreamĀ versionĀ 0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * oFono - GSM Telephony Stack for Linux
 
3
 *
 
4
 * Copyright (C) 2008-2009 Intel Corporation.  All rights reserved.
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 *
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#define _GNU_SOURCE
 
27
#include <string.h>
 
28
#include <stdio.h>
 
29
#include <stdlib.h>
 
30
#include <ctype.h>
 
31
 
 
32
#include <glib.h>
 
33
#include <gdbus.h>
 
34
 
 
35
#include "ofono.h"
 
36
 
 
37
#include "driver.h"
 
38
#include "common.h"
 
39
 
 
40
#define PHONEBOOK_INTERFACE "org.ofono.Phonebook"
 
41
#define LEN_MAX 128
 
42
#define TYPE_INTERNATIONAL 145
 
43
 
 
44
#define PHONEBOOK_FLAG_CACHED 0x1
 
45
 
 
46
enum phonebook_number_type {
 
47
        TEL_TYPE_HOME,
 
48
        TEL_TYPE_MOBILE,
 
49
        TEL_TYPE_FAX,
 
50
        TEL_TYPE_WORK,
 
51
        TEL_TYPE_OTHER,
 
52
};
 
53
 
 
54
struct phonebook_data {
 
55
        struct ofono_phonebook_ops *ops;
 
56
        DBusMessage *pending;
 
57
        int storage_index; /* go through all supported storage */
 
58
        int flags;
 
59
        GString *vcards; /* entries with vcard 3.0 format */
 
60
        GSList *merge_list; /* cache the entries that may need a merge */
 
61
};
 
62
 
 
63
struct phonebook_number {
 
64
        char *number;
 
65
        int type;
 
66
        enum phonebook_number_type category;
 
67
};
 
68
 
 
69
struct phonebook_person {
 
70
        GSList *number_list; /* one person may have more than one numbers */
 
71
        char *text;
 
72
        int hidden;
 
73
        char *group;
 
74
        char *email;
 
75
        char *sip_uri;
 
76
};
 
77
 
 
78
static const char *storage_support[] = { "SM", "ME", NULL };
 
79
static void export_phonebook(struct ofono_modem *modem);
 
80
 
 
81
static struct phonebook_data *phonebook_create()
 
82
{
 
83
        struct phonebook_data *phonebook;
 
84
        phonebook = g_try_new0(struct phonebook_data, 1);
 
85
 
 
86
        if (!phonebook)
 
87
                return NULL;
 
88
 
 
89
        phonebook->vcards = g_string_new(NULL);
 
90
 
 
91
        return phonebook;
 
92
}
 
93
 
 
94
static void phonebook_destroy(gpointer data)
 
95
{
 
96
        struct ofono_modem *modem = data;
 
97
        struct phonebook_data *phonebook = modem->phonebook;
 
98
 
 
99
        g_string_free(phonebook->vcards, TRUE);
 
100
 
 
101
        g_free(phonebook);
 
102
        modem->phonebook = NULL;
 
103
}
 
104
 
 
105
/* according to RFC 2425, the output string may need folding */
 
106
static void vcard_printf(GString *str, const char *fmt, ...)
 
107
{
 
108
        char buf[1024];
 
109
        va_list ap;
 
110
        int len_temp, line_number, i;
 
111
        unsigned int line_delimit = 75;
 
112
 
 
113
        va_start(ap, fmt);
 
114
        vsnprintf(buf, sizeof(buf), fmt, ap);
 
115
        va_end(ap);
 
116
 
 
117
        line_number = strlen(buf) / line_delimit + 1;
 
118
 
 
119
        for (i = 0; i < line_number; i++) {
 
120
                len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
 
121
                g_string_append_len(str,  buf + line_delimit * i, len_temp);
 
122
                if (i != line_number - 1)
 
123
                        g_string_append(str, "\r\n ");
 
124
        }
 
125
 
 
126
        g_string_append(str, "\r\n");
 
127
}
 
128
 
 
129
/* According to RFC 2426, we need escape following characters:
 
130
 *  '\n', '\r', ';', ',', '\'.
 
131
 */
 
132
static void add_slash(char *dest, const char *src, int len_max, int len)
 
133
{
 
134
        int i, j;
 
135
 
 
136
        for (i = 0, j = 0; i < len && j < len_max; i++, j++) {
 
137
                switch (src[i]) {
 
138
                case '\n':
 
139
                        dest[j++] = '\\';
 
140
                        dest[j] = 'n';
 
141
                        break;
 
142
                case '\r':
 
143
                        dest[j++] = '\\';
 
144
                        dest[j] = 'r';
 
145
                        break;
 
146
                case '\\':
 
147
                case ';':
 
148
                case ',':
 
149
                        dest[j++] = '\\';
 
150
                default:
 
151
                        dest[j] = src[i];
 
152
                        break;
 
153
                }
 
154
        }
 
155
        dest[j] = 0;
 
156
        return;
 
157
}
 
158
 
 
159
static void vcard_printf_begin(GString *vcards)
 
160
{
 
161
        vcard_printf(vcards, "BEGIN:VCARD");
 
162
        vcard_printf(vcards, "VERSION:3.0");
 
163
}
 
164
 
 
165
static void vcard_printf_text(GString *vcards, const char *text)
 
166
{
 
167
        char field[LEN_MAX];
 
168
        add_slash(field, text, LEN_MAX, strlen(text));
 
169
        vcard_printf(vcards, "FN:%s", field);
 
170
}
 
171
 
 
172
static void vcard_printf_number(GString *vcards, const char *number, int type,
 
173
                                        enum phonebook_number_type category)
 
174
{
 
175
        char *pref = "", *intl = "", *category_string = "";
 
176
        char buf[128];
 
177
 
 
178
        if (!number || !strlen(number) || !type)
 
179
                return;
 
180
 
 
181
        switch (category) {
 
182
        case TEL_TYPE_HOME:
 
183
                category_string = "HOME,VOICE";
 
184
                break;
 
185
        case TEL_TYPE_MOBILE:
 
186
                category_string = "CELL,VOICE";
 
187
                break;
 
188
        case TEL_TYPE_FAX:
 
189
                category_string = "FAX";
 
190
                break;
 
191
        case TEL_TYPE_WORK:
 
192
                category_string = "WORK,VOICE";
 
193
                break;
 
194
        case TEL_TYPE_OTHER:
 
195
                category_string = "VOICE";
 
196
                break;
 
197
        }
 
198
 
 
199
        if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
 
200
                intl = "+";
 
201
 
 
202
        sprintf(buf, "TEL;TYPE=\%s%s:\%s\%s", pref,
 
203
                        category_string, intl, number);
 
204
        vcard_printf(vcards, buf, number);
 
205
}
 
206
 
 
207
static void vcard_printf_group(GString *vcards, const char *group)
 
208
{
 
209
        int len = 0;
 
210
 
 
211
        if (group)
 
212
                len = strlen(group);
 
213
 
 
214
        if (len) {
 
215
                char field[LEN_MAX];
 
216
                add_slash(field, group, LEN_MAX, len);
 
217
                vcard_printf(vcards, "CATEGORIES:%s", field);
 
218
        }
 
219
}
 
220
 
 
221
static void vcard_printf_email(GString *vcards, const char *email)
 
222
{
 
223
        int len = 0;
 
224
 
 
225
        if (email)
 
226
                len = strlen(email);
 
227
 
 
228
        if (len) {
 
229
                char field[LEN_MAX];
 
230
                add_slash(field, email, LEN_MAX, len);
 
231
                vcard_printf(vcards,
 
232
                                "EMAIL;TYPE=INTERNET:%s", field);
 
233
        }
 
234
}
 
235
 
 
236
static void vcard_printf_sip_uri(GString *vcards, const char *sip_uri)
 
237
{
 
238
        int len = 0;
 
239
 
 
240
        if (sip_uri)
 
241
                len = strlen(sip_uri);
 
242
 
 
243
        if (len) {
 
244
                char field[LEN_MAX];
 
245
                add_slash(field, sip_uri, LEN_MAX, len);
 
246
                vcard_printf(vcards, "IMPP;TYPE=SIP:%s", field);
 
247
        }
 
248
}
 
249
 
 
250
static void vcard_printf_end(GString *vcards)
 
251
{
 
252
        vcard_printf(vcards, "END:VCARD");
 
253
        vcard_printf(vcards, "");
 
254
}
 
255
 
 
256
static void print_number(struct phonebook_number *pn, GString *vcards)
 
257
{
 
258
        vcard_printf_number(vcards, pn->number, pn->type, pn->category);
 
259
}
 
260
 
 
261
static void destroy_number(struct phonebook_number *pn)
 
262
{
 
263
        g_free(pn->number);
 
264
        g_free(pn);
 
265
}
 
266
 
 
267
static void print_merged_entry(struct phonebook_person *person, GString *vcards)
 
268
{
 
269
        vcard_printf_begin(vcards);
 
270
        vcard_printf_text(vcards, person->text);
 
271
 
 
272
        g_slist_foreach(person->number_list, (GFunc)print_number, vcards);
 
273
 
 
274
        vcard_printf_group(vcards, person->group);
 
275
        vcard_printf_email(vcards, person->email);
 
276
        vcard_printf_sip_uri(vcards, person->sip_uri);
 
277
        vcard_printf_end(vcards);
 
278
}
 
279
 
 
280
static void destroy_merged_entry(struct phonebook_person *person)
 
281
{
 
282
        g_free(person->text);
 
283
        g_free(person->group);
 
284
        g_free(person->email);
 
285
        g_free(person->sip_uri);
 
286
 
 
287
        g_slist_foreach(person->number_list, (GFunc)destroy_number, NULL);
 
288
        g_slist_free(person->number_list);
 
289
 
 
290
        g_free(person);
 
291
}
 
292
 
 
293
static DBusMessage *generate_export_entries_reply(struct ofono_modem *modem,
 
294
                                                        DBusMessage *msg)
 
295
{
 
296
        struct phonebook_data *phonebook = modem->phonebook;
 
297
        DBusMessage *reply;
 
298
        DBusMessageIter iter;
 
299
 
 
300
        reply = dbus_message_new_method_return(msg);
 
301
        if (!reply)
 
302
                return NULL;
 
303
 
 
304
        dbus_message_iter_init_append(reply, &iter);
 
305
        dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
 
306
                                        phonebook->vcards);
 
307
        return reply;
 
308
}
 
309
 
 
310
static gboolean need_merge(const char *text)
 
311
{
 
312
        int len;
 
313
        char c;
 
314
 
 
315
        if (!text)
 
316
                return FALSE;
 
317
 
 
318
        len = strlen(text);
 
319
 
 
320
        if (len < 2)
 
321
                return FALSE;
 
322
 
 
323
        c = tolower(text[len-1]);
 
324
 
 
325
        if ((text[len-2] == '/') &&
 
326
                        ((c == 'w') || (c == 'h') || (c == 'm') || (c == 'o')))
 
327
                return TRUE;
 
328
 
 
329
        return FALSE;
 
330
}
 
331
 
 
332
static void merge_field_generic(char **str1, const char *str2)
 
333
{
 
334
        if ((*str1 == NULL) && (str2 != NULL) && (strlen(str2) != 0))
 
335
                *str1 = g_strdup(str2);
 
336
}
 
337
 
 
338
static void merge_field_number(GSList **l, const char *number, int type, char c)
 
339
{
 
340
        struct phonebook_number *pn = g_new0(struct phonebook_number, 1);
 
341
        enum phonebook_number_type category;
 
342
 
 
343
        pn->number = g_strdup(number);
 
344
        pn->type = type;
 
345
        switch (tolower(c)) {
 
346
        case 'w':
 
347
                category = TEL_TYPE_WORK;
 
348
                break;
 
349
        case 'h':
 
350
                category = TEL_TYPE_HOME;
 
351
                break;
 
352
        case 'm':
 
353
                category = TEL_TYPE_MOBILE;
 
354
                break;
 
355
        case 'f':
 
356
                category = TEL_TYPE_FAX;
 
357
                break;
 
358
        case 'o':
 
359
        default:
 
360
                category = TEL_TYPE_OTHER;
 
361
                break;
 
362
        }
 
363
        pn->category = category;
 
364
        *l = g_slist_append(*l, pn);
 
365
}
 
366
 
 
367
void ofono_phonebook_entry(struct ofono_modem *modem, int index,
 
368
                                const char *number, int type,
 
369
                                const char *text, int hidden,
 
370
                                const char *group,
 
371
                                const char *adnumber, int adtype,
 
372
                                const char *secondtext, const char *email,
 
373
                                const char *sip_uri, const char *tel_uri)
 
374
{
 
375
        struct phonebook_data *phonebook = modem->phonebook;
 
376
 
 
377
        /* There's really nothing to do */
 
378
        if ((number == NULL || number[0] == '\0') &&
 
379
                        (text == NULL || text[0] == '\0'))
 
380
                return;
 
381
 
 
382
        /*
 
383
         * We need to collect all the entries that belong to one person,
 
384
         * so that only one vCard will be generated at last.
 
385
         * Entries only differ with '/w', '/h', '/m', etc. in field text
 
386
         * are deemed as entries of one person.
 
387
         */
 
388
        if (need_merge(text)) {
 
389
                GSList *l;
 
390
                size_t len_text = strlen(text) - 2;
 
391
                struct phonebook_person *person;
 
392
 
 
393
                for (l = phonebook->merge_list; l; l = l->next) {
 
394
                        person = l->data;
 
395
                        if (!strncmp(text, person->text, len_text) &&
 
396
                                        (strlen(person->text) == len_text))
 
397
                                break;
 
398
                }
 
399
 
 
400
                if (!l) {
 
401
                        person = g_new0(struct phonebook_person, 1);
 
402
                        phonebook->merge_list =
 
403
                                g_slist_prepend(phonebook->merge_list, person);
 
404
                        person->text = g_strndup(text, len_text);
 
405
                }
 
406
 
 
407
                merge_field_number(&(person->number_list), number, type,
 
408
                                        text[len_text + 1]);
 
409
                merge_field_number(&(person->number_list), adnumber, adtype,
 
410
                                        text[len_text + 1]);
 
411
 
 
412
                merge_field_generic(&(person->group), group);
 
413
                merge_field_generic(&(person->email), email);
 
414
                merge_field_generic(&(person->sip_uri), sip_uri);
 
415
 
 
416
                return;
 
417
        }
 
418
 
 
419
        vcard_printf_begin(phonebook->vcards);
 
420
 
 
421
        if (text == NULL || text[0] == '\0')
 
422
                vcard_printf_text(phonebook->vcards, number);
 
423
        else
 
424
                vcard_printf_text(phonebook->vcards, text);
 
425
 
 
426
        vcard_printf_number(phonebook->vcards, number, type, TEL_TYPE_OTHER);
 
427
        vcard_printf_number(phonebook->vcards, adnumber, adtype,
 
428
                                TEL_TYPE_OTHER);
 
429
        vcard_printf_group(phonebook->vcards, group);
 
430
        vcard_printf_email(phonebook->vcards, email);
 
431
        vcard_printf_sip_uri(phonebook->vcards, sip_uri);
 
432
        vcard_printf_end(phonebook->vcards);
 
433
}
 
434
 
 
435
static void export_phonebook_cb(const struct ofono_error *error, void *data)
 
436
{
 
437
        struct ofono_modem *modem = data;
 
438
        struct phonebook_data *phonebook = modem->phonebook;
 
439
 
 
440
        if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
 
441
                ofono_error("export_entries_one_storage_cb with %s failed",
 
442
                                storage_support[phonebook->storage_index]);
 
443
 
 
444
        /* convert the collected entries that are already merged to vcard */
 
445
        phonebook->merge_list = g_slist_reverse(phonebook->merge_list);
 
446
        g_slist_foreach(phonebook->merge_list, (GFunc)print_merged_entry,
 
447
                                phonebook->vcards);
 
448
        g_slist_foreach(phonebook->merge_list, (GFunc)destroy_merged_entry,
 
449
                                NULL);
 
450
        g_slist_free(phonebook->merge_list);
 
451
        phonebook->merge_list = NULL;
 
452
 
 
453
        phonebook->storage_index++;
 
454
        export_phonebook(modem);
 
455
        return;
 
456
}
 
457
 
 
458
static void export_phonebook(struct ofono_modem *modem)
 
459
{
 
460
        struct phonebook_data *phonebook = modem->phonebook;
 
461
        DBusMessage *reply;
 
462
        const char *pb = storage_support[phonebook->storage_index];
 
463
 
 
464
        if (pb) {
 
465
                phonebook->ops->export_entries(modem, pb,
 
466
                                                export_phonebook_cb, modem);
 
467
                return;
 
468
        }
 
469
 
 
470
        reply = generate_export_entries_reply(modem, phonebook->pending);
 
471
 
 
472
        if (!reply) {
 
473
                dbus_message_unref(phonebook->pending);
 
474
                return;
 
475
        }
 
476
 
 
477
        __ofono_dbus_pending_reply(&phonebook->pending, reply);
 
478
        phonebook->flags |= PHONEBOOK_FLAG_CACHED;
 
479
}
 
480
 
 
481
static DBusMessage *import_entries(DBusConnection *conn, DBusMessage *msg,
 
482
                                        void *data)
 
483
{
 
484
        struct ofono_modem *modem = data;
 
485
        struct phonebook_data *phonebook = modem->phonebook;
 
486
        DBusMessage *reply;
 
487
 
 
488
        if (phonebook->pending) {
 
489
                reply = __ofono_error_busy(phonebook->pending);
 
490
                g_dbus_send_message(conn, reply);
 
491
                return NULL;
 
492
        }
 
493
 
 
494
        if (phonebook->flags & PHONEBOOK_FLAG_CACHED) {
 
495
                reply = generate_export_entries_reply(modem, msg);
 
496
                g_dbus_send_message(conn, reply);
 
497
                return NULL;
 
498
        }
 
499
 
 
500
        g_string_set_size(phonebook->vcards, 0);
 
501
        phonebook->storage_index = 0;
 
502
 
 
503
        phonebook->pending = dbus_message_ref(msg);
 
504
        export_phonebook(modem);
 
505
 
 
506
        return NULL;
 
507
}
 
508
 
 
509
static GDBusMethodTable phonebook_methods[] = {
 
510
        { "Import",     "",     "s",    import_entries,
 
511
                                        G_DBUS_METHOD_FLAG_ASYNC },
 
512
        { }
 
513
};
 
514
 
 
515
static GDBusSignalTable phonebook_signals[] = {
 
516
        { }
 
517
};
 
518
 
 
519
int ofono_phonebook_register(struct ofono_modem *modem,
 
520
                                struct ofono_phonebook_ops *ops)
 
521
{
 
522
        DBusConnection *conn = ofono_dbus_get_connection();
 
523
 
 
524
        if (modem == NULL)
 
525
                return -1;
 
526
 
 
527
        if (ops == NULL)
 
528
                return -1;
 
529
 
 
530
        modem->phonebook = phonebook_create();
 
531
 
 
532
        if (modem->phonebook == NULL)
 
533
                return -1;
 
534
 
 
535
        modem->phonebook->ops = ops;
 
536
 
 
537
        if (!g_dbus_register_interface(conn, modem->path,
 
538
                                        PHONEBOOK_INTERFACE,
 
539
                                        phonebook_methods, phonebook_signals,
 
540
                                        NULL, modem, phonebook_destroy)) {
 
541
                ofono_error("Could not register Phonebook %s", modem->path);
 
542
 
 
543
                phonebook_destroy(modem->phonebook);
 
544
 
 
545
                return -1;
 
546
        }
 
547
 
 
548
        ofono_modem_add_interface(modem, PHONEBOOK_INTERFACE);
 
549
        return 0;
 
550
}
 
551
 
 
552
void ofono_phonebook_unregister(struct ofono_modem *modem)
 
553
{
 
554
        DBusConnection *conn = ofono_dbus_get_connection();
 
555
 
 
556
        if (modem->phonebook == NULL)
 
557
                return;
 
558
 
 
559
        ofono_modem_remove_interface(modem, PHONEBOOK_INTERFACE);
 
560
        g_dbus_unregister_interface(conn, modem->path,
 
561
                                        PHONEBOOK_INTERFACE);
 
562
}