APPLY_RSA
[apps/home/ug-memo-efl.git] / src / memo_autolink.c
1 /*
2 *
3 * Copyright 2012  Samsung Electronics Co., Ltd
4 *
5 * Licensed under the Flora License, Version 1.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *    http://www.tizenopensource.org/license
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <Elementary.h>
20 #include <dlog.h>
21 #include <gravel.h>
22 #include <extended-elm.h>
23 #include <assert.h>
24 #include <glib.h>
25 #include <memo_string.h>
26 #include <aul.h>
27 #include <appsvc.h>
28 #include <contacts-ug.h>
29 #include <memo_autolink.h>
30 #include <memo_ug.h>
31
32 static void _autolink_popup_response_cb(void *data, Evas_Object *obj, void *event_info);
33 /* BEGIN CONVERTE BETWEEN enum anchor_t AND string  */
34
35 static char *_autolink_anchor_type_to_string(enum anchor_t type)
36 {
37     switch (type) {
38     case ANCHOR_EMAIL:
39         return "email";
40     case ANCHOR_PHONE:
41         return "phone_num";
42     case ANCHOR_URL:
43         return "url";
44     default:
45         return "";
46     }
47 }
48
49 /* END CONVERTE BETWEEN enum anchor_t AND string  */
50
51 /* BEGIN POPUP CALLBACK */
52
53 static void _autolink_send_email_selected_cb(void *data, Evas_Object *obj, void *event_info)
54 {
55         struct autolink_data_t *ald = (struct autolink_data_t *)data;
56         service_h service = NULL;
57         service_create(&service);
58         service_add_extra_data(service, "RUN_TYPE", "5");
59         service_add_extra_data(service, "TO", ald->info);
60
61         ug_launch_common(service, UG_NAME_EMAIL);
62         _autolink_popup_response_cb(ald, ald->popup, NULL);
63 }
64
65 static void _autolink_send_message_selected_cb(void *data, Evas_Object *obj, void *event_info)
66 {
67     struct autolink_data_t *ald = (struct autolink_data_t *)data;
68
69     ug_launch_common_var(UG_NAME_MESSAGE, "TO", ald->info, NULL);
70     _autolink_popup_response_cb(ald, ald->popup, NULL);
71 }
72
73 static void _autolink_vioce_call_selected_cb(void *data, Evas_Object *obj, void *event_info)
74 {
75         struct autolink_data_t *ald = (struct autolink_data_t *)data;
76         bundle *bd = bundle_create();
77         char telnum[255] = {0,};
78
79         appsvc_set_operation(bd, APPSVC_OPERATION_CALL);
80         snprintf(telnum, sizeof(telnum), "tel:%s", ald->info);
81         appsvc_set_uri(bd, telnum);
82         appsvc_run_service(bd, 0, NULL, NULL);
83
84         bundle_free(bd);
85         _autolink_popup_response_cb(ald, ald->popup, NULL);
86 }
87
88 static void _autolink_video_call_selected_cb(void *data, Evas_Object *obj, void *event_info)
89 {
90         struct autolink_data_t *ald = (struct autolink_data_t *)data;
91         service_h service = NULL;
92         service_create(&service);
93         service_add_extra_data(service, "KEY_CALL_TYPE", "mo");
94         service_add_extra_data(service, "KEY_CALL_HANDLE", "1");
95         service_add_extra_data(service, "KEY_CALLING_PARTY_NUMBER", ald->info);
96         service_add_extra_data(service, "KEY_CLI_CAUSE", "-1");
97         service_add_extra_data(service, "KEY_FORWARDED", "-1");
98         service_set_package(service, AUL_NAME_VEDIO_CALL);
99         service_send_launch_request(service, NULL, NULL);
100
101         service_destroy(service);
102         _autolink_popup_response_cb(ald, ald->popup, NULL);
103 }
104
105 static void _autolink_email_add_to_contact_selected_cb(void *data, Evas_Object *obj,
106                                void *event_info)
107 {
108         struct autolink_data_t *ald = (struct autolink_data_t *)data;
109
110         char buf[10];
111         snprintf(buf, sizeof(buf), "%d", CT_UG_REQUEST_ADD_WITH_EMAIL);
112
113         service_h service = NULL;
114         service_create(&service);
115         service_add_extra_data(service, CT_UG_BUNDLE_TYPE, buf);
116         service_add_extra_data(service, CT_UG_BUNDLE_EMAIL, ald->info);
117
118         ug_launch_common(service, UG_CONTACTS_DETAILS);
119         _autolink_popup_response_cb(ald, ald->popup, NULL);
120 }
121
122 static void _autolink_phone_add_to_contact_selected_cb(void *data, Evas_Object *obj,
123                                void *event_info)
124 {
125         struct autolink_data_t *ald = (struct autolink_data_t *)data;
126
127         char buf[10];
128         snprintf(buf, sizeof(buf), "%d", CT_UG_REQUEST_ADD_WITH_NUM);
129
130         service_h service = NULL;
131         service_create(&service);
132         service_add_extra_data(service, CT_UG_BUNDLE_TYPE, buf);
133         service_add_extra_data(service, CT_UG_BUNDLE_NUM, ald->info);
134
135         ug_launch_common(service, UG_CONTACTS_DETAILS);
136         _autolink_popup_response_cb(ald, ald->popup, NULL);
137 }
138
139 /* END POPUP CALLBACK */
140
141 static struct anchor_popup_item_t g_email_list[] = {
142     {"IDS_MEMO_OPT_SEND_EMAIL", _autolink_send_email_selected_cb, "memo"},
143     {"IDS_MEMO_BODY_ADD_TO_CONTACT", _autolink_email_add_to_contact_selected_cb, "memo"},
144 };
145
146 static struct anchor_popup_item_t g_phone_list[] = {
147     {"IDS_COM_BODY_VOICE_CALL", _autolink_vioce_call_selected_cb, "sys_string"},
148     {"IDS_COM_BODY_SEND_MESSAGE", _autolink_send_message_selected_cb, "sys_string"},
149     {"IDS_COM_BODY_VIDEO_CALL", _autolink_video_call_selected_cb, "sys_string"},
150     {"IDS_MEMO_BODY_ADD_TO_CONTACT", _autolink_phone_add_to_contact_selected_cb, "memo"},
151 };
152
153 static char *_autolink_gl_label_get(void *data, Evas_Object *obj, const char *part)
154 {
155     char *label = (char *)data;
156     if (strcmp(part, "elm.text") == 0) {
157         return strdup(label);
158     }
159     return NULL;
160 }
161
162 static void _autolink_popup_response_cb(void *data, Evas_Object *obj, void *event_info)
163 {
164     PFUNC_ENTER;
165
166     assert(data != NULL);
167     struct autolink_data_t *ald = (struct autolink_data_t *)data;
168     if (ald->popup != NULL) {
169         evas_object_del(ald->popup);
170     }
171
172     assert(ald->info != NULL);
173     free(ald->info);
174     free(ald);
175
176     PFUNC_LEAVE;
177 }
178
179 void _autolink_popup_show(char *title, struct autolink_data_t *ald)
180 {
181     struct anchor_popup_item_t *list = NULL;
182     int count = 0;
183     Evas_Object *box = NULL;
184
185     switch (ald->type) {
186     case ANCHOR_EMAIL:
187         list = g_email_list;
188         count = ARRAY_SIZE(g_email_list);
189         break;
190     case ANCHOR_PHONE:
191         list = g_phone_list;
192         count = ARRAY_SIZE(g_phone_list);
193         break;
194     default:
195         break;
196     }
197
198     Evas_Object *popup = elm_popup_add(ald->win_main);
199     ald->popup = popup;
200     elm_object_part_text_set(popup, "title,text", title);
201     Evas_Object *btn1 = elm_button_add(popup);
202     elm_object_text_set(btn1, MEMO_I18N_CLOSE);
203     elm_object_part_content_set(popup, "button1", btn1);
204     evas_object_smart_callback_add(btn1, "clicked", _autolink_popup_response_cb, ald);
205     evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
206
207     static Elm_Genlist_Item_Class itc;
208     memset(&itc, 0, sizeof(Elm_Genlist_Item_Class));
209     itc.item_style = "1text";
210     itc.func.text_get = _autolink_gl_label_get;
211
212     Evas_Object *genlist = elm_genlist_add(popup);
213     evas_object_smart_callback_add(genlist, "selected", NULL, NULL);
214     evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
215     evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
216
217     int i = 0;
218     for (i = 0; i < count; ++i) {
219         elm_genlist_item_append(genlist, &itc, dgettext(list[i].domain, list[i].label),
220                     NULL, ELM_GENLIST_ITEM_NONE, list[i].response, ald);
221     }
222
223     evas_object_show(genlist);
224     box = elm_box_add(popup);
225     evas_object_size_hint_min_set(box, 0, (count>3 ? 3 : count)*71);
226     elm_box_pack_end(box, genlist);
227     evas_object_show(box);
228     elm_object_content_set(popup, box);
229     evas_object_show(popup);
230 }
231
232 void autolink_anchor_clicked_cb(void *data, Evas_Object *obj, void *event_info)
233 {
234     Elm_Entry_Anchor_Info *ei = (Elm_Entry_Anchor_Info *)event_info;
235     autolink_data *ald = SMALLOC(autolink_data);
236     RETIF(ald == NULL);
237
238     int info_len = strlen(ei->name) + 1;
239     ald->info = calloc(1, info_len);
240     if (ald->info == NULL) {
241         SFREE(ald);
242         return;
243     }
244
245     ald->win_main = (Evas_Object *)data;
246     /* retrieve anchor info, eg `url|www  phone_num|8888 email|abc@k.com` */
247     switch (ei->name[0]) {
248     case 'u':
249         ald->type = ANCHOR_URL;
250         break;
251     case 'p':
252         ald->type = ANCHOR_PHONE;
253         break;
254     case 'e':
255         ald->type = ANCHOR_EMAIL;
256         break;
257     default :
258         ald->type = NOT_ANCHOR;
259         break;
260     }
261     const char *start = ei->name + strlen(_autolink_anchor_type_to_string(ald->type)) +1;
262     const char *end = strchr(ei->name, ' ');
263     strncpy(ald->info, start, end - start);
264     LOGD("Anchor clicked (%s)\n", ald->info);
265
266     if ((ald->type == ANCHOR_EMAIL) || (ald->type == ANCHOR_PHONE)){
267         _autolink_popup_show(ald->info, ald);
268         return;
269     }
270
271     if (ald->type == ANCHOR_URL) {
272         aul_open_content(ald->info);    /* launch browser */
273     }
274     SFREE(ald->info);
275     SFREE(ald);
276 }
277
278 static Eina_Bool str_is_begin_with(const char *str, const char *prefix)
279 {
280     if (strlen(str) < strlen(prefix)) {
281         return EINA_FALSE;
282     }
283
284     int i = 0;
285     for (i = 0; i < strlen(prefix); ++i) {
286         if (str[i] != prefix[i]) {
287             return EINA_FALSE;
288         }
289     }
290
291     return EINA_TRUE;
292 }
293
294 /* TYPE CHECKER BEGIN */
295 /*
296  * [Checker Function Specification]
297  * These function get target type from beginning of string.
298  * The only parameter str is the string that want to be checked.
299  * Let's assume the returned value is r. There are 3 cases:
300  *     r > 0 : From beginning to r of str match the target type.
301  *     r = 0 : Str doesn't have target type, no longer check.
302  *     r < 0 : It doesn't match from beginning, but from -r position maybe matched.
303  *             It will be checked next time.
304  */
305 static int _autolink_url_checker(const char* str)
306 {
307     char *prefix[] = {"http://", "www.", "wap."};
308
309     int i = 0;
310     for (i = 0; i < ARRAY_SIZE(prefix); ++i) {
311         if (str_is_begin_with(str, prefix[i])) { /* Begin with specified prefix */
312             int j = 0;
313             while(str[j] != '\0' && !isspace(str[j])) {
314                 if (str[j] == '<') { /* tag begin */
315                     break;
316                 }
317                 j++;
318             }
319
320             if(j == strlen(prefix[i])) { /* only with prefix alone */
321                 break;
322             } else {
323                 return j;
324             }
325         }
326     }
327
328     /* not matched, get next position */
329 #if 0 /* Need correct */
330     int next = 0;
331     char *p = NULL;
332     for (i = 0; i < ARRAY_SIZE(prefix); ++i) {
333         p = strstr(str, prefix[i]);
334         if (p != NULL) {
335             if (next == 0) {
336                 next = p - str;
337             } else if (next > p - str) {
338                 next = p - str;
339             }
340         }
341     }
342 #endif
343
344     return -1;
345 }
346
347 static int _autolink_email_checker(const char* str)
348 {
349     char *p = NULL;
350     p = strchr(str, '@');
351
352     if (NULL == p) {
353         return 0;
354     } else if (p == str) { /* '@' at beginning */
355         return -1;
356     }
357
358     char next = p[1];
359     if (next == '@') { /* consecutive '@' */
360         return -(p + 2 - str);
361     } else if (next == '.') { /* "@." */
362         return -(p + 2 - str);
363     } else if (next == '\0') { /* '@' at end of string */
364         return -(p + 1 - str);
365     } else if (isspace(next) || next == '<') { /* '@' at end of word */
366         return -(p + 2 - str);
367     }
368
369     /* check if valid before @ */
370     char *q = p;
371     while (q >= str) {
372         if (isspace(*q)) {
373             return -(q + 1 - str);
374         }
375
376         if (*q == '>' || *q == '/') { /* tag end and other illega character */
377             return -(q + 1 - str);
378         }
379
380         if (*q == '.' && ( q == str || *(q - 1) == '.')) {
381             return -(q + 1 - str);
382         }
383
384         q--;
385     }
386
387     /* get the end position of email */
388     q = p + 1;
389     while (*q != '\0' && !isspace(*q)) {
390         if (*q == '.' && *(q + 1) == '.') {
391             break;
392         }
393
394         if (*q == '<') { /* tag begin */
395             break;
396         }
397
398         q++;
399     }
400     return q - str;
401 }
402
403 static int _autolink_phone_checker(const char *str)
404 {
405     /* search beginning */
406     int i = 0;
407     for (i = 0; str[i] != '\0'; ++i) {
408         /* digit, '+', '*' and '#' at the beginning of phone is valid. */
409         if (isdigit(str[i]) || str[i] == '+' || str[i] == '*' || str[i] == '#') {
410             break;
411         }
412     }
413
414     if (i > 0) {
415         return -i;
416     }
417
418     for (i = 1; str[i] != '\0'; ++i) {
419         if (isdigit(str[i]) || str[i] == '*' || str[i] == '#') {
420             /* NOP */
421         } else if (str[i] == '-') {
422             if (str[i - 1] == '-' || str[i - 1] == ' ') { /* "--" or " -" */
423                 i -= 1;
424                 break;
425             }
426         } else if (str[i] == ' ') {
427             if (str[i - 1] == '-') { /* "- " */
428                 i -= 1;
429                 break;
430             } else if (str[i - 1] == ' ' && isdigit(str[i + 1])) { /* consecutive ' ' */
431                 int j = 0;
432                 while (isdigit(str[i + 1 + j])) { ++j; }
433                 if (j > 5) {
434                     while (str[i] == ' ') { i--; }
435                     return i + 1;
436                 }
437             }
438         } else { /* other character */
439             break;
440         }
441     }
442
443     int len = i;
444     if (len < 3) {
445         return -len;
446     } else {
447         return len;
448     }
449 }
450
451 /* TYEP CHECKER END */
452
453 static void _autolink_box_anchor(GString *text, char *type, char *content)
454 {
455     char *buf = NULL;
456     if (strcmp(type, "phone_num") == 0) { /* remove '-' and ' ' in phone num. */
457         buf = (char *)malloc(strlen(content) + 1);
458         RETIF(buf == NULL);
459         int i = 0, j = 0;
460         while (content[i] != '\0') {
461             if (content[i] == '-' || content[i] == ' ') {
462                 ++i;
463             } else {
464                 buf[j] = content[i];
465                 ++i;
466                 ++j;
467             }
468         }
469         buf[j] = '\0';
470     } else {
471         buf = content;
472     }
473
474     g_string_append_printf(text,
475         "<color=#72b1f2FF><a href=%s|%s underline=on underline_color=#72b1f2FF>%s</a></color>",
476         type, buf, content);
477
478     if (buf != content) {
479         free(buf);
480     }
481 }
482
483 GString *autolink_add_anchor(const char *content)
484 {
485     assert(content != NULL);
486
487     const char *p = content;
488     char *gt = NULL;
489     GString *text = g_string_new("");
490     GString *buf = g_string_new("");
491
492     struct checker_t {
493         int (*check)(const char* str);
494         char *type;
495         Eina_Bool invoke;
496     } checkers[] = {
497         {_autolink_url_checker, "url", EINA_TRUE},
498         {_autolink_email_checker, "email", EINA_TRUE},
499         {_autolink_phone_checker, "phone_num", EINA_TRUE},
500     };
501
502     while (*p != '\0') {
503
504         if (p[0] == '<') {    /* tag begin */
505             gt = strchr(p, '>');    /* search tag end */
506             assert(gt != NULL);    /* assert '<' and '>' are matched */
507             g_string_append_len(text, p, gt - p + 1);
508             p = gt + 1;
509         }
510
511         int i = 0;
512         int r = 0;
513         int next_pos = -1;
514         for (i = 0; i < ARRAY_SIZE(checkers); ++i) {
515             if (checkers[i].invoke == EINA_TRUE) {
516                 r = checkers[i].check(p);
517                 if (r == 0) { /* no longer check */
518                     checkers[i].invoke = EINA_FALSE;
519                 } else if (r > 0) { /* Get */
520                     g_string_assign(buf, "");    /* reset buffer */
521                     g_string_append_len(buf, p, r);
522                     _autolink_box_anchor(text, checkers[i].type, buf->str);
523                     next_pos = r;
524                     break;
525                 } else { /* r < 0 */
526                     if (next_pos <= 0 || next_pos > -r) {
527                         next_pos = -r;
528                     }
529                 }
530             }
531         }
532
533         if (next_pos < 0) {
534             g_string_append(text, p);
535             break;
536         } else if ( i == ARRAY_SIZE(checkers)) {
537             g_string_append_len(text, p, next_pos);
538         }
539
540         p += next_pos;
541     }
542
543     g_string_free(buf, TRUE);
544     printf("\t\t********** %s\n", text->str);
545
546     return text;
547 }