7b38fdb902ed4b996ff39c637570e38b9ded7b8e
[platform/core/connectivity/bluetooth-agent.git] / map-agent / map_bmessage.c
1 /*
2  * Bluetooth-agent
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:  Hocheol Seo <hocheol.seo@samsung.com>
7  *               Girishashok Joshi <girish.joshi@samsung.com>
8  *               Chanyeol Park <chanyeol.park@samsung.com>
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *              http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <glib.h>
27
28 #include <ITapiNetText.h>
29
30 #include <map_bmessage.h>
31 #include <bluetooth_map_agent.h>
32
33 #define CRLF_LEN 2
34 #define BT_SMS_DATA_MAX_LEN 165
35
36 #define BMSG_TAG "BEGIN:BMSG\r\n"
37 #define VER_TAG "VERSION:"
38 #define STATUS_TAG "STATUS:"
39 #define TYPE_TAG "TYPE:"
40 #define FOLDER_TAG "FOLDER:"
41 #define VCARD_BEGIN_TAG "BEGIN:VCARD\r\n"
42 #define VCARD_END_TAG "END:VCARD\r\n"
43 #define VCARD_N_TAG "N:"
44 #define VCARD_FN_TAG "FN:"
45 #define VCARD_TEL_TAG "TEL:"
46 #define VCARD_EMAIL_TAG "EMAIL:"
47 #define BENV_TAG "BEGIN:BENV\r\n"
48 #define BBODY_TAG "BEGIN:BBODY\r\n"
49 #define MSG_TAG "BEGIN:MSG\r\n"
50 #define PARTID_TAG "PARTID:"
51 #define ENCODING_TAG "ENCODING:"
52 #define CHARSET_TAG "CHARSET:"
53 #define LANGUAGE_TAG "LANGUAGE:"
54 #define LENGTH_TAG "LENGTH:"
55
56 static guint8 g_enc_lvl = 1;
57
58 void print_bmsg(struct bmsg_data *bmsg)
59 {
60         FN_START;
61         if (bmsg == NULL)
62                 return;
63
64         struct benv_data *env_data = NULL;
65
66         DBG("bmsg->version = %s", bmsg->version);
67         DBG("bmsg->status = %s", bmsg->status);
68         DBG("bmsg->type = %s", bmsg->type);
69         DBG_SECURE("bmsg->folder = %s", bmsg->folder);
70
71         if (bmsg->originator_vcard_data) {
72                 DBG_SECURE("bmsg->originator_vcard_data->version = %s",
73                                                 bmsg->originator_vcard_data->version);
74                 DBG_SECURE("bmsg->originator_vcard_data->n = %s",
75                                                 bmsg->originator_vcard_data->n);
76         }
77
78         int i = 0;
79         env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
80         while (env_data != NULL) {
81
82                 DBG("env_data = %d", env_data->encapsulation_level);
83                 int k = 0;
84                 struct bmsg_vcard *rvcard;
85
86                 rvcard = g_slist_nth_data(env_data->recipient_vcard, k);
87
88                 while (rvcard != NULL) {
89                         k++;
90
91                         if (rvcard->version != NULL)
92                                 DBG("vcard->version = %s\n", rvcard->version);
93                         if (rvcard->n != NULL)
94                                 DBG_SECURE("vcard->n = %s\n", rvcard->n);
95                         if (rvcard->fn != NULL)
96                                 DBG_SECURE("vcard->fn = %s\n", rvcard->fn);
97                         if (rvcard->tel != NULL)
98                                 DBG_SECURE("vcard->tel = %s\n", rvcard->tel);
99                         if (rvcard->email != NULL)
100                                 DBG_SECURE("vcard->email = %s\n", rvcard->email);
101
102                         rvcard = g_slist_nth_data(env_data->recipient_vcard, k);
103                 }
104
105                 if (env_data->body_content != NULL) {
106                         DBG_SECURE("env_data->body_content->length = %"
107                                                 G_GUINT64_FORMAT "\n",
108                                                 env_data->body_content->length);
109                         DBG_SECURE("env_data->body_content->msg = %s\n",
110                                                 env_data->body_content->msg);
111                 }
112
113                 i++;
114
115                 if (i > 2)
116                         break;
117
118                 env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
119         }
120         FN_END;
121 }
122
123 static gchar *__bt_unpack_gsm7bit_msg(const char* pdu, int in_len)
124 {
125         FN_START;
126         int i;
127         int pos = 0;
128         int shift = 0;
129         gchar data[BT_SMS_DATA_MAX_LEN + 1] = {0,};
130
131         for (i = 0; i < in_len; i++) {
132                 if (shift == 0) {
133                         data[i] = pdu[pos] & 0x7F;
134
135                         shift = 7;
136                         pos++;
137                 } else {
138                         data[i] = (pdu[pos - 1] >> shift) |
139                                                 (pdu[pos] << (8 - shift));
140                         data[i] &= 0x7F;
141
142                         shift--;
143                         if (shift > 0)
144                                 pos++;
145                 }
146         }
147
148         DBG_SECURE("msg = %s\n", data);
149         FN_END;
150         return g_strdup(data);
151 }
152
153 static gchar *__bt_get_msg_body_from_pdu(gchar *pdu, guint64 pdu_len)
154 {
155         FN_START;
156         int index = 0;
157         int i;
158         int j = 0;
159         int dcs;
160         int udh = 0;
161         int coding_scheme;
162         int phone_num_len = 0;
163         char temp[3];
164         char msg_data[BT_SMS_DATA_MAX_LEN + 1] = {0,};
165         unsigned char pdu_data[TAPI_NETTEXT_MSG_SIZE_MAX] = {0,};
166
167         for (i = 0; i < (pdu_len - 1);) {
168                 snprintf(temp, sizeof(temp), "%c%c", pdu[i], pdu[i+1]);
169
170                 pdu_data[j] = g_ascii_strtoull(temp, NULL, 16);
171                 DBG("pdu_data = %02x\n", pdu_data[j]);
172                 j++;
173                 i = i + 2;
174         }
175
176         DBG("pdu[%d] = %x\n", index, pdu_data[index]);
177         if (pdu[index] == 0x00)
178                 index++;
179         else
180                 index = index + pdu_data[index];
181
182         /* TP-MTI */
183         index = index + 1;
184
185         if (pdu_data[index] & 0x40)
186                 udh = 1;
187
188         DBG("udh = %d", udh);
189
190         /* TP-MR */
191         index = index + 1;
192
193         /* phone number length */
194         index = index + 1;
195         DBG("pdu[%d] = %x\n", index, pdu_data[index]);
196
197         if ((pdu_data[index] % 2) == 0)
198                 phone_num_len = pdu_data[index] / 2;
199         else
200                 phone_num_len = pdu_data[index] / 2 + 1;
201
202         DBG("phone_num_len [%d]\n", phone_num_len);
203
204         /* phone number type */
205         index = index + 1;
206
207         /* phone_num_len/2 encoded phone num length */
208         index = index + phone_num_len;
209
210         /* TP-PID */
211         index = index + 1;
212
213         /* TP-DCS */
214         index = index + 1;
215
216         dcs = pdu_data[index];
217         coding_scheme = (dcs & 0x0C) >> 2;
218         DBG("coding_scheme = %d\n", coding_scheme);
219
220         /* TP-VP */
221         index = index + 1;
222
223         /* TP-UDL */
224         index = index + 1;
225         int udl = pdu_data[index];
226         DBG("udl = %x\n", udl);
227
228         /* message body */
229         index = index + 1;
230
231         memcpy(msg_data, (void*)&pdu_data[index], udl);
232
233         FN_END;
234         return __bt_unpack_gsm7bit_msg(msg_data, udl);
235 }
236
237 static gchar *__bt_get_valid_number(gchar* num)
238 {
239         FN_START;
240         int len;
241         int i = 0;
242         int j = 0;
243         gchar *valid_num;
244
245         if (!num)
246                 return NULL;
247
248         len = strlen(num);
249
250         valid_num = g_malloc0(len + 1);
251         retv_if(valid_num == NULL, NULL);
252
253         for (i = 0, j = 0; i < len; i++) {
254
255                 if (num[i] != '-') {
256                         valid_num[j] = num[i];
257                         j++;
258                 }
259         }
260
261         valid_num[j] = '\0';
262
263         FN_END;
264         return valid_num;
265 }
266
267 char *bmsg_get_msg_folder(struct bmsg_data *bmsg)
268 {
269         FN_START;
270         return g_strdup(bmsg->folder);
271 }
272
273 char *bmsg_get_msg_body(struct bmsg_data *bmsg, gboolean utf)
274 {
275         FN_START;
276         struct benv_data *env_data;
277         int i = 0;
278
279         env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
280
281         while (env_data != NULL) {
282                 if (env_data->body_content != NULL) {
283                         DBG_SECURE("env_data->body_content->msg = %s\n",
284                                                 env_data->body_content->msg);
285                         DBG_SECURE("env_data->body_content->length = %"
286                                                 G_GUINT64_FORMAT "\n",
287                                                 env_data->body_content->length);
288
289                         if (utf == FALSE) {
290                                 return __bt_get_msg_body_from_pdu(
291                                                 env_data->body_content->msg,
292                                                 env_data->body_content->length);
293                         } else {
294                                 return g_strndup(
295                                                 env_data->body_content->msg,
296                                                 env_data->body_content->length);
297                         }
298                 }
299
300                 i++;
301                 if (i > 2)
302                         break;
303
304                 env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
305         }
306
307         FN_END;
308         return NULL;
309 }
310
311 char *get_line(char *string, int *len)
312 {
313         int i = 0;
314         while (string[i] != '\n' && string[i] != '\0')
315                 i++;
316
317         *len = i;
318         return g_strndup(string, i - 1);
319 }
320
321 gboolean bmsg_parse_msg_body(const char *message,
322                         char **body, char **subject)
323 {
324         FN_START;
325         DBG("Message: %s", message);
326         char *temp = (char *)message;
327         char *line = NULL;
328         int len;
329
330         *subject = NULL;
331         *body = NULL;
332         while ((line = get_line(temp, &len)) != NULL) {
333                 if (!g_ascii_strncasecmp(line, "Date:", strlen("Date:"))) {
334                         //Currently nothing to be done
335                 } else if (!g_ascii_strncasecmp(line, "Subject:", strlen("Subject:"))) {
336                         char *sub = line + strlen("Subject:");
337                         while (*sub == ' ')
338                                 sub++;
339                         DBG("Subject: %s", sub);
340                         *subject = g_strdup(sub);
341                 } else if (!g_ascii_strncasecmp(line, "From:", strlen("From:"))) {
342                         //Currently nothing to be done
343                 } else if (!g_ascii_strncasecmp(line, "To:", strlen("To:"))) {
344                         //Currently nothing to be done
345                 } else {
346                         DBG("BODY: %s", temp);
347                         *body = g_strdup(temp);
348                         g_free(line);
349                         break;
350                 }
351
352                 while ((temp[len] == '\r' || temp[len] == '\n') && temp[len] != '\0')
353                         len++;
354
355                 temp = temp + len;
356
357                 g_free(line);
358         }
359         return TRUE;
360         FN_END;
361 }
362
363 GSList *bmsg_get_msg_recepients(struct bmsg_data *bmsg, int msg_type)
364 {
365         FN_START;
366         struct benv_data *env_data;
367         GSList *receiver = NULL;
368         int i = 0;
369
370         env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
371
372         while (env_data != NULL) {
373
374                 DBG("env_data = %d", env_data->encapsulation_level);
375                 int k = 0;
376                 struct bmsg_vcard *rvcard;
377
378                 rvcard = g_slist_nth_data(env_data->recipient_vcard, k);
379                 while (rvcard != NULL) {
380                         k++;
381
382                         if (msg_type == BT_MAP_ID_SMS) {
383                                 if (rvcard->tel != NULL) {
384                                         DBG_SECURE("vcard->tel = %s\n", rvcard->tel);
385                                         receiver = g_slist_append(receiver,
386                                                                         rvcard->tel);
387                                 }
388                         } else {
389                                 if (rvcard->email != NULL) {
390                                         DBG_SECURE("vcard->email = %s\n", rvcard->email);
391                                         receiver = g_slist_append(receiver,
392                                                                         rvcard->email);
393                                 }
394                         }
395                         rvcard = g_slist_nth_data(env_data->recipient_vcard, k);
396                 }
397
398                 i++;
399                 if (i > 2)
400                         break;
401
402                 env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
403         }
404         FN_END;
405         return receiver;
406 }
407
408 void bmsg_free_vcard_data(struct bmsg_vcard *vcard_data)
409 {
410         FN_START;
411         if (vcard_data == NULL)
412                 return;
413
414         g_free(vcard_data->version);
415         g_free(vcard_data->n);
416         g_free(vcard_data->fn);
417         g_free(vcard_data->tel);
418         g_free(vcard_data->email);
419         g_free(vcard_data);
420         FN_END;
421         return;
422 }
423
424 void bmsg_free_bmsg(struct bmsg_data *bmsg)
425 {
426         FN_START;
427         struct benv_data *env_data;
428         int i = 0;
429
430         if (bmsg == NULL)
431                 return;
432
433         g_free(bmsg->version);
434         g_free(bmsg->status);
435         g_free(bmsg->type);
436         g_free(bmsg->folder);
437         bmsg_free_vcard_data(bmsg->originator_vcard_data);
438
439         if (bmsg->envelope_data == NULL)
440                 goto done;
441
442         if (bmsg->envelope_data->env_data == NULL)
443                 goto done;
444
445         env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
446         while (env_data != NULL) {
447
448                 DBG("env_data = %d", env_data->encapsulation_level);
449                 int k = 0;
450                 struct bmsg_vcard *rvcard;
451
452                 rvcard = g_slist_nth_data(env_data->recipient_vcard, k);
453
454                 while (rvcard != NULL) {
455                         k++;
456                         bmsg_free_vcard_data(rvcard);
457                         rvcard = g_slist_nth_data(env_data->recipient_vcard, k);
458                 }
459
460                 if (env_data->body_content != NULL) {
461                         g_free(env_data->body_content->encoding);
462                         g_free(env_data->body_content->charset);
463                         g_free(env_data->body_content->language);
464                         g_free(env_data->body_content->msg);
465                         g_free(env_data->body_content);
466                 }
467
468                 g_free(env_data);
469                 i++;
470
471                 env_data = g_slist_nth_data(bmsg->envelope_data->env_data, i);
472         }
473
474         FN_END;
475 done:
476         g_free(bmsg);
477 }
478
479 gchar *bmsg_get_parse_sub_block(char **sub_block_data, char *element)
480 {
481         FN_START;
482         gchar *start;
483         gchar *end;
484         gchar *block_start;
485         gchar *block_end;
486         gchar *sub_block = NULL;
487         size_t offset;
488         size_t len;
489
490         start = g_strdup_printf("BEGIN:%s\r\n", element);
491         end = g_strdup_printf("END:%s\r\n", element);
492         offset = strlen(start);
493
494         block_start = g_strstr_len(*sub_block_data, offset, start);
495         if (block_start == NULL)
496                 goto done;
497
498         if (!g_strcmp0(start, VCARD_BEGIN_TAG))
499                 block_end = g_strstr_len(*sub_block_data, -1,  end);
500         else
501                 block_end = g_strrstr(*sub_block_data, end);
502
503         if (block_end == NULL)
504                 goto done;
505
506         len = block_end - block_start - offset;
507         sub_block = g_strndup(block_start + offset, len);
508         *sub_block_data = *sub_block_data + strlen(sub_block) + strlen(start) +
509                                                                  strlen(end);
510 done:
511         g_free(start);
512         g_free(end);
513         FN_END;
514         return sub_block;
515 }
516
517 gchar *bmsg_get_tag_data(char **block_data, char *element)
518 {
519         FN_START;
520         gchar *end = "\r\n";
521         gchar *block_start;
522         gchar *block_end;
523         gchar *sub_block;
524         size_t offset;
525         size_t len;
526
527         if (*block_data == NULL || element == NULL)
528                 return NULL;
529
530         block_start = g_strstr_len(*block_data, -1, element);
531         if (block_start == NULL)
532                 return NULL;
533
534         offset = strlen(element);
535
536         block_end = g_strstr_len(block_start+offset, -1, end);
537         if (block_end == NULL)
538                 return NULL;
539
540         len = block_end - block_start - offset;
541         sub_block = g_strndup(block_start + offset, len);
542         *block_data = *block_data + offset + len + CRLF_LEN;
543         FN_END;
544         return sub_block;
545 }
546
547 struct bmsg_bbody *bmsg_get_bbody_data(gchar *block_data)
548 {
549         FN_START;
550         struct bmsg_bbody *bbody;
551         gchar *temp;
552         gchar *bbody_block_data_start = block_data;
553
554         bbody = g_new0(struct bmsg_bbody, 1);
555
556         temp = bmsg_get_tag_data(&block_data, PARTID_TAG);
557         if (temp != NULL) {
558                 bbody->part_id = (guint16)g_ascii_strtoull(temp, NULL, 10);
559                 g_free(temp);
560         }
561
562         bbody->encoding = bmsg_get_tag_data(&block_data, ENCODING_TAG);
563         bbody->charset = bmsg_get_tag_data(&block_data, CHARSET_TAG);
564         bbody->language = bmsg_get_tag_data(&block_data, LANGUAGE_TAG);
565
566         temp = bmsg_get_tag_data(&block_data, LENGTH_TAG);
567
568         if (temp != NULL) {
569                 bbody->length = g_ascii_strtoull(temp, NULL, 10);
570                 g_free(temp);
571         }
572
573         bbody->msg = bmsg_get_parse_sub_block(&block_data, "MSG");
574
575         g_free(bbody_block_data_start);
576         FN_END;
577         return bbody;
578 }
579
580 struct bmsg_vcard *bmsg_get_vcard_data(gchar *sub_block_data)
581 {
582         FN_START;
583         struct bmsg_vcard *vcard;
584         gchar *num;
585         gchar *vcard_block_data_start = sub_block_data;
586
587         vcard = g_new0(struct bmsg_vcard, 1);
588
589         vcard->version = bmsg_get_tag_data(&sub_block_data, VER_TAG);
590         vcard->n = bmsg_get_tag_data(&sub_block_data, VCARD_N_TAG);
591         vcard->fn = bmsg_get_tag_data(&sub_block_data, VCARD_FN_TAG);
592         num = bmsg_get_tag_data(&sub_block_data, VCARD_TEL_TAG);
593         vcard->tel = __bt_get_valid_number(num);
594         vcard->email = bmsg_get_tag_data(&sub_block_data, VCARD_EMAIL_TAG);
595
596         g_free(vcard_block_data_start);
597         g_free(num);
598         FN_END;
599         return vcard;
600 }
601
602 struct benv_data *bmsg_get_env_encapsulation_data(gchar **sub_block_data)
603 {
604         FN_START;
605         gchar *is_valid;
606         gchar *bbody_data = NULL;
607
608         is_valid = g_strstr_len(*sub_block_data, strlen(VCARD_BEGIN_TAG),
609                                                         VCARD_BEGIN_TAG);
610         if (is_valid == NULL)
611                 return NULL;
612
613         if (g_enc_lvl > 3)
614                 return NULL;
615
616         struct benv_data *rec_data = g_new0(struct benv_data, 1);
617
618         rec_data->encapsulation_level = g_enc_lvl;
619         g_enc_lvl++;
620
621         while (is_valid != NULL) {
622                 gchar *vcard_data = NULL;
623                 struct bmsg_vcard *vcard;
624
625                 vcard_data = bmsg_get_parse_sub_block(sub_block_data, "VCARD");
626                 if (vcard_data == NULL) {
627                         ERR("parse error\n");
628                         g_free(rec_data);
629                         return NULL;
630                 }
631                 vcard = bmsg_get_vcard_data(vcard_data);
632
633                 rec_data->recipient_vcard = g_slist_append(
634                                                 rec_data->recipient_vcard,
635                                                 vcard);
636
637                 is_valid = g_strstr_len(*sub_block_data,
638                                                 strlen(VCARD_BEGIN_TAG),
639                                                 VCARD_BEGIN_TAG);
640         }
641
642         is_valid = g_strstr_len(*sub_block_data, strlen(BBODY_TAG), BBODY_TAG);
643
644         if (!is_valid)
645                 return rec_data;
646
647         bbody_data = bmsg_get_parse_sub_block(sub_block_data, "BBODY");
648         if (bbody_data == NULL) {
649                 ERR("parse error\n");
650                 return rec_data;
651         }
652
653         rec_data->body_content = bmsg_get_bbody_data(bbody_data);
654         FN_END;
655         return rec_data;
656 }
657
658 struct bmsg_envelope *bmsg_get_envelope_data(gchar **block_data)
659 {
660         FN_START;
661         gchar *sub_block_data;
662         struct bmsg_envelope *envelope_data;
663         struct benv_data *rec_data;
664
665         envelope_data = g_new0(struct bmsg_envelope, 1);
666
667         sub_block_data = bmsg_get_parse_sub_block(block_data, "BENV");
668
669         while (sub_block_data) {
670                 char *next_sub_block_data;
671                 char *save_sub_block_data = sub_block_data;
672                 rec_data = bmsg_get_env_encapsulation_data(&sub_block_data);
673
674                 while (rec_data) {
675                         envelope_data->env_data = g_slist_append(
676                                                         envelope_data->env_data,
677                                                         rec_data);
678
679                         rec_data = bmsg_get_env_encapsulation_data(
680                                                         &sub_block_data);
681                 }
682                 next_sub_block_data = bmsg_get_parse_sub_block(&sub_block_data,
683                                                                 "BENV");
684                 g_free(save_sub_block_data);
685                 sub_block_data = next_sub_block_data;
686         }
687         g_free(sub_block_data);
688         FN_END;
689         return envelope_data;
690 }
691
692 struct bmsg_data *bmsg_parse(gchar *buf)
693 {
694         FN_START;
695         gchar *block_data;
696         gchar *sub_block_data;
697         gchar *block_data_start;
698         struct bmsg_data *bmsg;
699
700         g_enc_lvl = 1;
701
702         block_data = bmsg_get_parse_sub_block(&buf, "BMSG");
703         if (block_data == NULL)
704                 return NULL;
705
706         block_data_start = block_data;
707
708         bmsg = g_new0(struct bmsg_data, 1);
709
710         bmsg->version = bmsg_get_tag_data(&block_data, VER_TAG);
711         if (bmsg->version == NULL)
712                 goto parse_fail;
713
714         bmsg->status = bmsg_get_tag_data(&block_data, STATUS_TAG);
715         if (bmsg->status == NULL)
716                 goto parse_fail;
717
718         bmsg->type = bmsg_get_tag_data(&block_data, TYPE_TAG);
719         if (bmsg->type == NULL)
720                 goto parse_fail;
721
722         bmsg->folder = bmsg_get_tag_data(&block_data, FOLDER_TAG);
723         if (bmsg->folder == NULL)
724                 goto parse_fail;
725
726         sub_block_data = bmsg_get_parse_sub_block(&block_data, "VCARD");
727         if (sub_block_data != NULL) {
728                 bmsg->originator_vcard_data = bmsg_get_vcard_data(sub_block_data);
729                 if (bmsg->originator_vcard_data == NULL)
730                         goto parse_fail;
731         }
732
733         bmsg->envelope_data = bmsg_get_envelope_data(&block_data);
734         if (bmsg->envelope_data == NULL)
735                 goto parse_fail;
736
737         g_free(block_data_start);
738
739         DBG("Parse done");
740         print_bmsg(bmsg);
741         FN_END;
742         return bmsg;
743
744 parse_fail:
745         g_free(block_data_start);
746
747         ERR("Parse fail");
748         bmsg_free_bmsg(bmsg);
749         FN_END;
750         return NULL;
751 }
752