/* * Contacts Service * * Copyright (c) 2010 - 2012 Samsung Electronics Co., Ltd. All rights reserved. * * Contact: Youngjae Shin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include #include #include #include #include #include #include "cts-internal.h" #include "cts-schema.h" #include "cts-sqlite.h" #include "cts-socket.h" #include "cts-normalize.h" #include "cts-inotify.h" #include "cts-vcard.h" #include "cts-pthread.h" #include "cts-types.h" #include "cts-restriction.h" #include "cts-utils.h" static const char *CTS_NOTI_CONTACT_CHANGED=CTS_NOTI_CONTACT_CHANGED_DEF; static const char *CTS_NOTI_PLOG_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_PLOG_CHANGED"; static const char *CTS_NOTI_FAVORITE_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_FAVOR_CHANGED"; static const char *CTS_NOTI_SPEEDDIAL_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_SPEED_CHANGED"; static const char *CTS_NOTI_ADDRBOOK_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_AB_CHANGED"; static const char *CTS_NOTI_GROUP_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_GROUP_CHANGED"; static const char *CTS_NOTI_GROUP_RELATION_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_GROUP_REL_CHANGED"; static const char *CTS_NOTI_MISSED_CALL_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_MISSED_CHANGED"; static const char *CTS_NOTI_LINK_CHANGED="/opt/data/contacts-svc/.CONTACTS_SVC_LINK_CHANGED"; static const char *CTS_VCONF_SORTING_ORDER=VCONFKEY_CONTACTS_SVC_NAME_SORTING_ORDER; static const char *CTS_VCONF_DISPLAY_ORDER=VCONFKEY_CONTACTS_SVC_NAME_DISPLAY_ORDER; static int transaction_count = 0; static int transaction_ver = 0; static bool version_up=false; static bool contact_change=false; static bool plog_change=false; static bool missed_change=false; static bool favor_change=false; static bool speed_change=false; static bool addrbook_change=false; static bool group_change=false; static bool group_rel_change=false; static bool link_change=false; static int name_sorting_order = -1; static int name_display_order = -1; static int default_lang = -1; static void cts_vconf_callback(keynode_t *key, void *data) { //if(!strcmp(vconf_keynode_get_name(key), CTS_VCONF_SORTING_ORDER)); if (CTS_ORDER_OF_SORTING == (int)data) name_sorting_order = vconf_keynode_get_int(key); else if (CTS_ORDER_OF_DISPLAY == (int)data) name_display_order = vconf_keynode_get_int(key); else if (CTS_ORDER_OF_DISPLAY + 1 == (int)data) default_lang = vconf_keynode_get_int(key); } int cts_get_default_language(void) { if (default_lang < 0) { int ret; ret = vconf_get_int(CTS_VCONF_DEFAULT_LANGUAGE, &default_lang); warn_if(ret < 0, "vconf_get_int() Failed(%d)", ret); } return default_lang; } void cts_set_contact_noti(void) { contact_change = true; } void cts_set_plog_noti(void) { plog_change = true; } void cts_set_missed_call_noti(void) { missed_change = true; } void cts_set_favor_noti(void) { favor_change = true; } void cts_set_speed_noti(void) { speed_change = true; } void cts_set_addrbook_noti(void) { addrbook_change = true; } void cts_set_group_noti(void) { group_change = true; } void cts_set_group_rel_noti(void) { group_rel_change = true; } void cts_set_link_noti(void) { link_change = true; } static inline void cts_noti_publish_contact_change(void) { int fd = open(CTS_NOTI_CONTACT_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); contact_change = false; } } static inline void cts_noti_publish_plog_change(void) { int fd = open(CTS_NOTI_PLOG_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); plog_change = false; } } static inline void cts_noti_publish_missed_call_change(void) { int fd = open(CTS_NOTI_MISSED_CALL_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); missed_change = false; } } static inline void cts_noti_publish_favor_change(void) { int fd = open(CTS_NOTI_FAVORITE_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); favor_change = false; } } static inline void cts_noti_publish_speed_change(void) { int fd = open(CTS_NOTI_SPEEDDIAL_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); speed_change = false; } } static inline void cts_noti_publish_addrbook_change(void) { int fd = open(CTS_NOTI_ADDRBOOK_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); addrbook_change = false; } } static inline void cts_noti_publish_group_change(void) { int fd = open(CTS_NOTI_GROUP_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); group_change = false; } } static inline void cts_noti_publish_group_rel_change(void) { int fd = open(CTS_NOTI_GROUP_RELATION_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); group_rel_change = false; } } static inline void cts_noti_publish_link_change(void) { int fd = open(CTS_NOTI_LINK_CHANGED, O_TRUNC | O_RDWR); if (0 <= fd) { close(fd); link_change = false; } } #define CTS_COMMIT_TRY_MAX 500000 // For 3second API int contacts_svc_begin_trans(void) { int ret = -1, progress; cts_mutex_lock(CTS_MUTEX_TRANSACTION); if (transaction_count <= 0) { ret = cts_query_exec("BEGIN IMMEDIATE TRANSACTION"); //taken 600ms progress = 100000; while (CTS_ERR_DB_LOCK == ret && progress 0) { ret = write(dest_fd, buf, size); if (ret <= 0) { if (EINTR == errno) continue; else { ERR("write() Failed(%d)", errno); if (ENOSPC == errno) ret = CTS_ERR_NO_SPACE; else ret = CTS_ERR_IO_ERR; close(src_fd); close(dest_fd); unlink(dest); return ret; } } } fchown(dest_fd, getuid(), CTS_SECURITY_FILE_GROUP); fchmod(dest_fd, CTS_SECURITY_DEFAULT_PERMISSION); close(src_fd); close(dest_fd); return CTS_SUCCESS; } int cts_contact_add_image_file(int img_type, int index, char *src_img, char *dest_name, int dest_size) { int ret; char *ext; char dest[CTS_IMG_PATH_SIZE_MAX]; retvm_if(NULL == src_img, CTS_ERR_ARG_INVALID, "img_path is NULL"); ext = strrchr(src_img, '.'); if (NULL == ext || strchr(ext, '/')) ext = ""; snprintf(dest, sizeof(dest), "%s/%d-%d%s", CTS_IMAGE_LOCATION, index, img_type, ext); ret = cts_copy_file(src_img, dest); retvm_if(CTS_SUCCESS != ret, ret, "cts_copy_file() Failed(%d)", ret); snprintf(dest_name, dest_size, "%d-%d%s", index, img_type, ext); return CTS_SUCCESS; } int cts_contact_update_image_file(int img_type, int index, char *src_img, char *dest_name, int dest_size) { int ret; ret = cts_contact_delete_image_file(img_type, index); retvm_if(CTS_SUCCESS != ret && CTS_ERR_DB_RECORD_NOT_FOUND != ret, ret, "cts_contact_delete_image_file() Failed(%d)", ret); if (src_img) { ret = cts_contact_add_image_file(img_type, index, src_img, dest_name, dest_size); retvm_if(CTS_SUCCESS != ret, ret, "cts_contact_add_image_file() Failed(%d)", ret); } return ret; } /* * This function is for MY profile and group image. */ char* cts_get_img(const char *dir, int index, char *dest, int dest_size) { DIR *dp; char *ret_val; struct dirent *file_info; char tmp_path[CTS_IMG_PATH_SIZE_MAX] = {0}; if (0 < index) snprintf(tmp_path, sizeof(tmp_path), "%d", index); dp = opendir(dir); if (dp) { while ((file_info = readdir(dp)) != NULL) { CTS_DBG("file = %s", file_info->d_name); if ('.' != *file_info->d_name) { if (0 == index || !strncmp(tmp_path, file_info->d_name, strlen(tmp_path))) { if (dest) { snprintf(dest, dest_size, "%s/%s", dir, file_info->d_name); ret_val = dest; } else { snprintf(tmp_path, sizeof(tmp_path), "%s/%s", dir, file_info->d_name); ret_val = strdup(tmp_path); } closedir(dp); return ret_val; } } } closedir(dp); } return NULL; } /* * This function is for MY profile and group image. */ int cts_set_img(const char *dir, int index, const char *path) { int ret; char dest[CTS_IMG_PATH_SIZE_MAX]; if (cts_get_img(dir, index, dest, sizeof(dest))) { if (path && 0 == strcmp(dest, path)) return CTS_SUCCESS; ret = unlink(dest); retvm_if(ret < 0, CTS_ERR_FAIL, "unlink(%s) Failed(%d)", dest, errno); } if (path) { char *ext; ext = strrchr(path, '.'); if (NULL == ext || strchr(ext, '/')) ext = ""; snprintf(dest, sizeof(dest), "%s/%d%s", dir, index, ext); ret = cts_copy_file(path, dest); retvm_if(CTS_SUCCESS != ret, ret, "cts_copy_file() Failed(%d)", ret); } return CTS_SUCCESS; } int cts_update_contact_changed_time(int contact_id) { int ret; char query[CTS_SQL_MIN_LEN]; snprintf(query, sizeof(query), "UPDATE %s SET changed_ver=%d, changed_time=%d WHERE contact_id=%d", CTS_TABLE_CONTACTS, cts_get_next_ver(), (int)time(NULL), contact_id); ret = cts_query_exec(query); retvm_if(CTS_SUCCESS != ret, ret, "cts_query_exec() Failed(%d)", ret); cts_set_contact_noti(); return CTS_SUCCESS; } API int contacts_svc_save_image(cts_img_t img_type, int index, char *src_img) { int ret; char query[CTS_SQL_MIN_LEN]; char dest_img[CTS_SQL_MIN_LEN]; cts_stmt stmt; ret = contacts_svc_begin_trans(); retvm_if(ret, ret, "contacts_svc_begin_trans() Failed(%d)", ret); dest_img[0] = '\0'; ret = cts_contact_update_image_file(img_type, index, src_img, dest_img, sizeof(dest_img)); if (CTS_SUCCESS != ret) { ERR("cts_contact_update_image_file() Failed(%d)", ret); contacts_svc_end_trans(false); return ret; } snprintf(query, sizeof(query), "UPDATE %s SET changed_ver=%d, changed_time=%d, image%d=? WHERE contact_id=%d", CTS_TABLE_CONTACTS, cts_get_next_ver(), (int)time(NULL), img_type, index); stmt = cts_query_prepare(query); if (NULL == stmt) { ERR("cts_query_prepare() Failed"); contacts_svc_end_trans(false); return CTS_ERR_DB_FAILED; } if(*dest_img) cts_stmt_bind_text(stmt, 1, dest_img); ret = cts_stmt_step(stmt); warn_if(CTS_SUCCESS != ret, "cts_stmt_step() Failed(%d)", ret); cts_stmt_finalize(stmt); cts_set_contact_noti(); ret = contacts_svc_end_trans(true); retvm_if(ret < CTS_SUCCESS, ret, "contacts_svc_end_trans() Failed(%d)", ret); return CTS_SUCCESS; } API int contacts_svc_count_with_int(cts_count_int_op op_code, int search_value) { int ret; cts_stmt stmt; char query[CTS_SQL_MIN_LEN] = {0}; switch ((int)op_code) { case CTS_GET_COUNT_CONTACTS_IN_ADDRESSBOOK: snprintf(query, sizeof(query), "SELECT COUNT(DISTINCT person_id) FROM %s " "WHERE addrbook_id = ?", CTS_TABLE_CONTACTS); break; case CTS_GET_COUNT_CONTACTS_IN_GROUP: snprintf(query, sizeof(query), "SELECT COUNT(DISTINCT person_id) " "FROM %s A, %s B ON A.contact_id = B.contact_id " "WHERE group_id = ?", CTS_TABLE_GROUPING_INFO, CTS_TABLE_CONTACTS); break; case CTS_GET_COUNT_NO_GROUP_CONTACTS_IN_ADDRESSBOOK: snprintf(query, sizeof(query), "SELECT COUNT(DISTINCT person_id) FROM %s A " "WHERE addrbook_id = ? AND NOT EXISTS " "(SELECT contact_id FROM %s WHERE contact_id=A.contact_id LIMIT 1)", CTS_TABLE_CONTACTS, CTS_TABLE_GROUPING_INFO); break; case CTS_GET_COUNT_GROUPS_IN_ADDRESSBOOK: // FIXME: should be removed (for OSP): CTS_GET_COUNT_GROUPS_IN_ADDRESSBOOK snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s WHERE addrbook_id = ?", CTS_TABLE_GROUPS); break; default: ERR("Invalid parameter : The op_code(%d) is not supported", op_code); return CTS_ERR_ARG_INVALID; } stmt = cts_query_prepare(query); retvm_if(NULL == stmt, CTS_ERR_DB_FAILED, "cts_query_prepare() Failed"); cts_stmt_bind_int(stmt, 1, search_value); ret = cts_stmt_get_first_int_result(stmt); if (CTS_ERR_DB_RECORD_NOT_FOUND == ret) return 0; else return ret; } API int contacts_svc_count(cts_count_op op_code) { int ret; char query[CTS_SQL_MIN_LEN] = {0}; switch ((int)op_code) { case CTS_GET_ALL_CONTACT: if (cts_restriction_get_permit()) snprintf(query, sizeof(query),"SELECT COUNT(*) FROM %s", CTS_TABLE_PERSONS); else snprintf(query, sizeof(query),"SELECT COUNT(*) FROM %s " "WHERE person_id NOT IN " "(SELECT contact_id FROM %s WHERE is_restricted = 1)", CTS_TABLE_PERSONS, CTS_TABLE_DATA); break; case CTS_GET_COUNT_SDN: snprintf(query, sizeof(query),"SELECT COUNT(*) FROM %s", CTS_TABLE_SIM_SERVICES); break; case CTS_GET_ALL_PHONELOG: snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s", CTS_TABLE_PHONELOGS); break; case CTS_GET_UNSEEN_MISSED_CALL: snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s " "WHERE log_type = %d OR log_type = %d", CTS_TABLE_PHONELOGS, CTS_PLOG_TYPE_VOICE_INCOMMING_UNSEEN, CTS_PLOG_TYPE_VIDEO_INCOMMING_UNSEEN); break; case CTS_GET_INCOMING_CALL: snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s " "WHERE log_type = %d OR log_type = %d", CTS_TABLE_PHONELOGS, CTS_PLOG_TYPE_VOICE_INCOMMING, CTS_PLOG_TYPE_VIDEO_INCOMMING); break; case CTS_GET_OUTGOING_CALL: snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s " "WHERE log_type = %d OR log_type = %d", CTS_TABLE_PHONELOGS, CTS_PLOG_TYPE_VOICE_OUTGOING, CTS_PLOG_TYPE_VIDEO_OUTGOING); break; case CTS_GET_MISSED_CALL: snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s " "WHERE log_type BETWEEN %d AND %d", CTS_TABLE_PHONELOGS, CTS_PLOG_TYPE_VOICE_INCOMMING_UNSEEN, CTS_PLOG_TYPE_VIDEO_INCOMMING_SEEN); break; case CTS_GET_COUNT_ALL_GROUP: // FIXME: should be removed (for OSP): CTS_GET_COUNT_ALL_GROUP snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s", CTS_TABLE_GROUPS); break; default: ERR("Invalid parameter : The op_code(%d) is not supported", op_code); return CTS_ERR_ARG_INVALID; } ret = cts_query_get_first_int_result(query); if (CTS_ERR_DB_RECORD_NOT_FOUND == ret) return 0; else return ret; } int cts_convert_nicknames2textlist(GSList *src, char *dest, int dest_size) { int len; GSList *nick_repeat = src; cts_nickname *nick_data; retvm_if(dest_size <= 0 || NULL == dest, CTS_ERR_ARG_INVALID, "The parameter is Invalid. dest = %p, dest_size = %d", dest, dest_size); len = 0; dest[0] = '\0'; while (nick_repeat) { if (NULL != (nick_data = (cts_nickname *)nick_repeat->data) && nick_data->nick) { if (!nick_data->deleted) len += snprintf(dest+len, dest_size-len, "%s,", nick_data->nick); } nick_repeat = nick_repeat->next; } len = strlen(dest); if (len) dest[len - 1] = '\0'; else return CTS_ERR_NO_DATA; return CTS_SUCCESS; } GSList* cts_convert_textlist2nicknames(char *text_list) { char temp[CTS_SQL_MAX_LEN], *text_start, *text_end; GSList *ret_list = NULL; cts_nickname *result; snprintf(temp, sizeof(temp), "%s", text_list); text_start = temp; while (text_start) { text_end = strchr(text_start, ','); if (text_end) *text_end = '\0'; result = (cts_nickname *)contacts_svc_value_new(CTS_VALUE_NICKNAME); if (result) { result->embedded = true; result->nick = strdup(text_start); } ret_list = g_slist_append(ret_list, result); if (text_end) { *text_end = ','; text_start = text_end+1; } else text_start = NULL; } return ret_list; } API int contacts_svc_import_sim(void) { int ret; cts_mutex_lock(CTS_MUTEX_SOCKET_FD); ret = cts_request_sim_import(); cts_mutex_unlock(CTS_MUTEX_SOCKET_FD); return ret; } API int contacts_svc_export_sim(int index) { int ret; retvm_if(index <= 0, CTS_ERR_ARG_INVALID, "index is invalid", index); cts_mutex_lock(CTS_MUTEX_SOCKET_FD); ret = cts_request_sim_export(index); cts_mutex_unlock(CTS_MUTEX_SOCKET_FD); return ret; } int cts_increase_outgoing_count(int person_id) { int ret; char query[CTS_SQL_MIN_LEN]; snprintf(query, sizeof(query), "UPDATE %s SET outgoing_count = outgoing_count + 1 WHERE person_id = %d", CTS_TABLE_PERSONS, person_id); ret = cts_query_exec(query); retvm_if(CTS_SUCCESS != ret, ret, "cts_query_exec() Failed(%d)", ret); return CTS_SUCCESS; } API int contacts_svc_reset_outgoing_count(int person_id) { int ret ; char query[CTS_SQL_MAX_LEN]; snprintf(query, sizeof(query), "UPDATE %s SET outgoing_count = 0 WHERE person_id = %d", CTS_TABLE_PERSONS, person_id); ret = contacts_svc_begin_trans(); retvm_if(ret, ret, "contacts_svc_begin_trans() Failed(%d)", ret); ret = cts_query_exec(query); if (CTS_SUCCESS != ret) { ERR("cts_query_exec() Failed(%d)", ret); contacts_svc_end_trans(false); return ret; } ret = contacts_svc_end_trans(true); if (ret < CTS_SUCCESS) return ret; else return CTS_SUCCESS; } #ifdef CTS_TIMECHECK double cts_set_start_time(void) { struct timeval tv; double curtime; gettimeofday(&tv, NULL); curtime = tv.tv_sec * 1000 + (double)tv.tv_usec/1000; return curtime; } double cts_exec_time(double start) { double end = cts_set_start_time(); return (end - start - correction); } int cts_init_time(void) { double temp_t; temp_t = cts_set_start_time(); correction = cts_exec_time(temp_t); return 0; } #endif