/* * $Id: imc.c 1808 2007-03-10 17:36:19Z bogdan_iancu $ * * imc module - instant messaging conferencing implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of openser, a free SIP server. * * openser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * openser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2006-10-06 first version (anca) */ #include #include #include #include #include #include #include #include #include "../../db/db.h" #include "../../db/db_res.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" #include "../../timer.h" #include "../../mem/shm_mem.h" #include "../../db/db.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../../parser/contact/parse_contact.h" #include "../../resolve.h" #include "../../hash_func.h" #include "../../mi/mi.h" #include "../tm/tm_load.h" #include "imc_mng.h" #include "imc_cmd.h" MODULE_VERSION /** parameters */ db_con_t *imc_db = NULL; db_func_t imc_dbf; str db_url = {0, 0}; char* rooms_table = "imc_rooms"; char* members_table = "imc_members"; imc_hentry_p _imc_htable = NULL; int imc_hash_size = 4; char *imc_cmd_start_str= IMC_CMD_START_STR; char imc_cmd_start_char; /** module functions */ static int mod_init(void); static int child_init(int); static int imc_manager(struct sip_msg*, char *, char *); static struct mi_root* imc_mi_list_rooms(struct mi_root* cmd, void* param); static struct mi_root* imc_mi_list_members(struct mi_root* cmd, void* param); void destroy(void); /** TM bind */ struct tm_binds tmb; /** TM callback function */ void inv_callback( struct cell *t, int type, struct tmcb_params *ps); static cmd_export_t cmds[]={ {"imc_manager", imc_manager, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ {"db_url", STR_PARAM, &db_url.s}, {"hash_size", INT_PARAM, &imc_hash_size}, {"imc_cmd_start_char", STR_PARAM, &imc_cmd_start_str}, {"rooms_table", STR_PARAM, &rooms_table}, {"members_table", STR_PARAM, &members_table}, {0,0,0} }; #ifdef STATISTICS #include "../../statistics.h" stat_var* imc_active_rooms; stat_export_t imc_stats[] = { {"active_rooms" , 0, &imc_active_rooms }, {0,0,0} }; #endif static mi_export_t mi_cmds[] = { { "imc_list_rooms", imc_mi_list_rooms, MI_NO_INPUT_FLAG, 0, 0 }, { "imc_list_members", imc_mi_list_members, 0, 0, 0 }, { 0, 0, 0, 0, 0} }; /** module exports */ struct module_exports exports= { "imc", /* module name */ DEFAULT_DLFLAGS, /* dlopen flags */ cmds, /* exported commands */ params, /* exported parameters */ #ifdef STATISTICS imc_stats, #else 0, /* exported statistics */ #endif mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ mod_init, /* mod init */ (response_function) 0, /* response handler */ (destroy_function) destroy, /* destroy function */ child_init /* child init */ }; /** * the initiating function */ int add_from_db(void) { imc_member_p member = NULL; int i, j, flag; db_key_t mq_result_cols[4], mquery_cols[2]; db_key_t rq_result_cols[4]; db_val_t mquery_vals[2]; db_res_t *r_res= NULL; db_res_t *m_res= NULL; db_row_t *m_row = NULL, *r_row = NULL; db_val_t *m_row_vals, *r_row_vals = NULL; str name, domain; imc_room_p room = NULL; int er_ret = -1; rq_result_cols[0] ="name"; rq_result_cols[1] ="domain"; rq_result_cols[2] ="flag"; mq_result_cols[0] ="username"; mq_result_cols[1] ="domain"; mq_result_cols[2] ="flag"; mquery_cols[0] = "room"; mquery_vals[0].type = DB_STR; mquery_vals[0].nul = 0; if(imc_dbf.use_table(imc_db, rooms_table)< 0) { LOG(L_ERR, "imc:mod_init:ERROR in use table\n"); return -1; } if(imc_dbf.query(imc_db,0, 0, 0, rq_result_cols,0, 3, 0,&r_res)< 0) { LOG(L_ERR, "imc:mod_init:ERROR while querrying table\n"); return -1; } if(r_res && r_res->n<=0) { LOG(L_INFO, "imc:mod_init:the query returned no result\n"); imc_dbf.free_result(imc_db, r_res); r_res = NULL; return 0; } DBG("imc:mod_init: found %d rooms\n", r_res->n); for(i =0 ; i< r_res->n ; i++) { /*add rooms*/ r_row = &r_res->rows[i]; r_row_vals = ROW_VALUES(r_row); name.s = r_row_vals[0].val.str_val.s; name.len = strlen(name.s); domain.s = r_row_vals[1].val.str_val.s; domain.len = strlen(domain.s); flag = r_row_vals[2].val.int_val; room = imc_add_room(&name, &domain, flag); if(room == NULL) { LOG(L_ERR, "imc:mod_init:ERROR while adding room\n "); goto error; } /* add members */ if(imc_dbf.use_table(imc_db, members_table)< 0) { LOG(L_ERR, "imc:mod_init:ERROR in use table\n "); goto error; } mquery_vals[0].val.str_val= room->uri; if(imc_dbf.query(imc_db, mquery_cols, 0, mquery_vals, mq_result_cols, 1, 3, 0, &m_res)< 0) { LOG(L_ERR, "imc:mod_init:ERROR while querrying table\n"); goto error; } if(m_res && m_res->n <=0) { LOG(L_INFO, "imc:mod_init:the query returned no result\n"); er_ret = 0; goto error; /* each room must have at least one member*/ } for(j =0; j< m_res->n; j++) { m_row = &m_res->rows[j]; m_row_vals = ROW_VALUES(m_row); name.s =m_row_vals[0].val.str_val.s; name.len = strlen(name.s); domain.s = m_row_vals[1].val.str_val.s; domain.len = strlen(domain.s); flag = m_row_vals[2].val.int_val ; DBG("imc:mod_init: Adding memeber: [name]=%.*s [domain]=%.*s" " in [room]= %.*s\n",name.len, name.s, domain.len,domain.s, room->uri.len, room->uri.s); member = imc_add_member(room, &name, &domain, flag); if(member == NULL) { LOG(L_ERR, "imc:mod_init:ERROR while adding member\n "); goto error; } imc_release_room(room); } if(m_res) { imc_dbf.free_result(imc_db, m_res); m_res = NULL; } } if(imc_dbf.use_table(imc_db, members_table)< 0) { LOG(L_ERR, "imc:mod_init:ERROR in use table\n "); goto error; } if(imc_dbf.delete(imc_db, 0, 0 , 0, 0) < 0) { LOG(L_ERR, "imc:mod_init:ERROR while deleting information from db\n"); goto error; } if(imc_dbf.use_table(imc_db, rooms_table)< 0) { LOG(L_ERR, "imc:mod_init:ERROR in use table\n "); goto error; } if(imc_dbf.delete(imc_db, 0, 0 , 0, 0) < 0) { LOG(L_ERR, "imc:mod_init:ERROR while deleting information from db\n"); goto error; } if(r_res) { imc_dbf.free_result(imc_db, r_res); r_res = NULL; } if(m_res) { imc_dbf.free_result(imc_db, m_res); m_res = NULL; } return 0; error: if(r_res) { imc_dbf.free_result(imc_db, r_res); r_res = NULL; } if(m_res) { imc_dbf.free_result(imc_db, m_res); m_res = NULL; } if(room) imc_release_room(room); return er_ret; } static int mod_init(void) { DBG("imc: initializing ...\n"); if(imc_hash_size<=0) { LOG(L_ERR, "imc:mod_init: error - invalid hash size\n"); return -1; } imc_hash_size = 1<content_length) { LOG(L_ERR,"imc:imc_manager: error - no Content-Length\n"); goto error; } body.len = get_content_length( msg ); if(body.len <= 0) { DBG("imc:imc_manager: empty body!\n"); goto error; } if(parse_sip_msg_uri(msg)<0) { LOG(L_ERR,"imc:imc_manager: error - parsing r-uri\n"); goto error; } pto_uri=&msg->parsed_uri; if(parse_from_header(msg)<0) { LOG(L_ERR,"imc:imc_manager: error - parsing From header\n"); goto error; } pfrom = (struct to_body*)msg->from->parsed; if(parse_uri(pfrom->uri.s, pfrom->uri.len, &from_uri)<0){ LOG(L_ERR,"imc:imc_manager: error - parsing From URI\n"); goto error; } pfrom_uri=&from_uri; if(body.s[0]== imc_cmd_start_char) { DBG("imc:imc_manager: found command\n"); if(imc_parse_cmd(body.s, body.len, &cmd)<0) { LOG(L_ERR,"imc:imc_manager: error parsing imc cmd!\n"); goto error; } switch(cmd.type) { case IMC_CMDID_CREATE: if(imc_handle_create(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'create'\n"); goto error; } break; case IMC_CMDID_JOIN: if(imc_handle_join(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'join'\n"); goto error; } break; case IMC_CMDID_INVITE: if(imc_handle_invite(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'invite'\n"); goto error; } break; case IMC_CMDID_ACCEPT: if(imc_handle_accept(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'accept'\n"); goto error; } break; case IMC_CMDID_DENY: if(imc_handle_deny(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'deny'\n"); goto error; } break; case IMC_CMDID_REMOVE: if(imc_handle_remove(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'remove'\n"); goto error; } break; case IMC_CMDID_EXIT: if(imc_handle_exit(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'exit'\n"); goto error; } break; case IMC_CMDID_LIST: if(imc_handle_list(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'list'\n"); goto error; } break; case IMC_CMDID_DESTROY: if(imc_handle_destroy(msg, &cmd, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'destroy'\n"); goto error; } break; case IMC_CMDID_HELP: if(imc_handle_help(msg, &cmd, &pfrom->uri, (msg->new_uri.s)?&msg->new_uri:&msg->first_line.u.request.uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'help'\n"); goto error; } break; default: if(imc_handle_unknown(msg, &cmd, &pfrom->uri, (msg->new_uri.s)?&msg->new_uri:&msg->first_line.u.request.uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'unknown'\n"); goto error; } } goto done; } if(imc_handle_message(msg, &body, pfrom_uri, pto_uri)<0) { LOG(L_ERR, "imc:imc_manager: ERROR while handling 'message'\n"); goto error; } done: return 1; error: return -1; } /** * destroy module */ void destroy(void) { imc_room_p irp = NULL; imc_member_p member = NULL; int i; db_key_t mq_cols[4]; db_val_t mq_vals[4]; db_key_t rq_cols[4]; db_val_t rq_vals[4]; DBG("imc: destroy module ...\n"); if(imc_db==NULL) goto done; mq_cols[0] ="username"; mq_vals[0].type = DB_STR; mq_vals[0].nul = 0; mq_cols[1] ="domain"; mq_vals[1].type = DB_STR; mq_vals[1].nul = 0; mq_cols[2] ="flag"; mq_vals[2].type = DB_INT; mq_vals[2].nul = 0; mq_cols[3] ="room"; mq_vals[3].type = DB_STR; mq_vals[3].nul = 0; rq_cols[0] ="name"; rq_vals[0].type = DB_STR; rq_vals[0].nul = 0; rq_cols[1] ="domain"; rq_vals[1].type = DB_STR; rq_vals[1].nul = 0; rq_cols[2] ="flag"; rq_vals[2].type = DB_INT; rq_vals[2].nul = 0; for(i=0; iname; rq_vals[1].val.str_val = irp->domain; rq_vals[2].val.int_val = irp->flags; if(imc_dbf.use_table(imc_db, rooms_table)< 0) { LOG(L_ERR, "imc:destroy: ERROR in use_table\n"); return; } if(imc_dbf.insert(imc_db, rq_cols, rq_vals, 3)<0) { LOG(L_ERR, "imc:destroy: ERROR while inserting into table" " imc_rooms\n"); return; } DBG("imc:destroy: room %d %.*s\n", i, irp->name.len, irp->name.s); member = irp->members; while(member) { mq_vals[0].val.str_val = member->user; mq_vals[1].val.str_val = member->domain; mq_vals[2].val.int_val = member->flags; mq_vals[3].val.str_val = irp->uri; if(imc_dbf.use_table(imc_db, members_table)< 0) { LOG(L_ERR, "imc:destroy: ERROR in use_table\n"); return; } if(imc_dbf.insert(imc_db, mq_cols, mq_vals, 4)<0) { LOG(L_ERR, "imc:destroy: ERROR while inserting into table" " imc_rooms\n"); return; } member = member->next; } irp = irp->next; } } done: imc_htable_destroy(); } /************************* MI ***********************/ static struct mi_root* imc_mi_list_rooms(struct mi_root* cmd_tree, void* param) { int i, len; struct mi_root* rpl_tree= NULL; struct mi_node* rpl= NULL; struct mi_node* node= NULL; struct mi_attr* attr= NULL; imc_room_p irp = NULL; char* p = NULL; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if(rpl_tree == NULL) return 0; rpl = &rpl_tree->node; for(i=0; iuri.s, irp->uri.len); if(attr == NULL) goto error; p = int2str(irp->nr_of_members, &len); attr= add_mi_attr(node, 0, "MEMBERS", 7,p, len ); if(attr == NULL) goto error; attr= add_mi_attr(node, MI_DUP_VALUE, "OWNER", 5, irp->members->uri.s, irp->members->uri.len); if(attr == NULL) goto error; irp = irp->next; } lock_release(&_imc_htable[i].lock); } return rpl_tree; error: lock_release(&_imc_htable[i].lock); free_mi_tree(rpl_tree); return 0; } static struct mi_root* imc_mi_list_members(struct mi_root* cmd_tree, void* param) { int i, len; struct mi_root* rpl_tree = NULL; struct mi_node* node= NULL; struct mi_node* node_r= NULL; struct mi_attr* attr= NULL; char rnbuf[256]; str room_name; imc_room_p room; struct sip_uri inv_uri, *pinv_uri; imc_member_p imp=NULL; char* p = NULL; node= cmd_tree->node.kids; if(node == NULL|| node->next!=NULL) return 0; /* room name */ room_name.s = rnbuf; room_name.len= node->value.len; memcpy(room_name.s, node->value.s, node->value.len); if(room_name.s == NULL || room_name.len == 0) { LOG(L_ERR, "IMC:imc_mi_list_members: error - no room name!\n"); return init_mi_tree( 404, "room name not found", 19); } rnbuf[room_name.len] = '\0'; if(*room_name.s=='\0' || *room_name.s=='.') { LOG(L_INFO, "IMC:imc_mi_list_members: empty room name\n"); return init_mi_tree( 400, "empty param", 11); } /* find room */ parse_uri(room_name.s,room_name.len, &inv_uri); pinv_uri=&inv_uri; room=imc_get_room(&pinv_uri->user, &pinv_uri->host); if(room==NULL) { LOG(L_ERR,"IMC:imc_mi_list_members: no such room!\n"); return init_mi_tree( 404, "no such room", 14); } rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if(rpl_tree == NULL) return 0; node_r = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, "ROOM", 4, room_name.s, room_name.len); if(node_r == NULL) goto error; imp = room->members; i=0; while(imp) { i++; node = add_mi_node_child(node_r, MI_DUP_VALUE, "MEMBER",6, imp->uri.s, imp->uri.len); if(node == NULL) goto error; imp = imp->next; } p = int2str(i, &len); attr= add_mi_attr(node_r, MI_DUP_VALUE, "NR_OF_MEMBERS", 13, p, len); if(attr == 0) goto error; imc_release_room(room); return rpl_tree; error: imc_release_room(room); free_mi_tree(rpl_tree); return 0; }