/** * free a Jabber connections pool structure * - jcp : pointer to the Jabber connections pool structure */ void xj_jcon_pool_free(xj_jcon_pool jcp) { int i; if(jcp == NULL) return; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif if(jcp->ojc != NULL) { for(i=0; i<jcp->len; i++) { if(jcp->ojc[i] != NULL) xj_jcon_free(jcp->ojc[i]); } _M_FREE(jcp->ojc); } if(jcp->jmqueue.jsm != NULL) _M_FREE(jcp->jmqueue.jsm); if(jcp->jmqueue.ojc != NULL) _M_FREE(jcp->jmqueue.ojc); if(jcp->jmqueue.expire != NULL) _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp); }
/** * check for expired connections */ void xj_worker_check_jcons(xj_wlist jwl, xj_jcon_pool jcp, int ltime, fd_set *pset) { int i; xj_jconf jcf; for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; if(jcp->ojc[i]->jkey->flag==XJ_FLAG_OPEN && jcp->ojc[i]->expire > ltime) continue; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: connection expired for <%.*s> \n", _xj_pid, jcp->ojc[i]->jkey->id->len, jcp->ojc[i]->jkey->id->s); #endif xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name, XJ_DMSG_INF_JOFFLINE, NULL); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: connection's close flag =%d\n", _xj_pid, jcp->ojc[i]->jkey->flag); #endif // CLEAN JAB_WLIST xj_wlist_del(jwl, jcp->ojc[i]->jkey, _xj_pid); // looking for open conference rooms #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: having %d open conferences\n", _xj_pid, jcp->ojc[i]->nrjconf); #endif while(jcp->ojc[i]->nrjconf > 0) { if((jcf=delpos234(jcp->ojc[i]->jconf,0))!=NULL) { // get out of room xj_jcon_jconf_presence(jcp->ojc[i],jcf, "unavailable", NULL); xj_jconf_free(jcf); } jcp->ojc[i]->nrjconf--; } // send offline presence to all subscribers if(jcp->ojc[i]->plist) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: sending 'terminated' status to SIP" " subscriber\n", _xj_pid); #endif xj_pres_list_notifyall(jcp->ojc[i]->plist, XJ_PS_TERMINATED); } FD_CLR(jcp->ojc[i]->sock, pset); xj_jcon_disconnect(jcp->ojc[i]); xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; } }
/** * remove the connection associated with 'id' from pool * - jcp : pointer to the Jabber connections pool structure * - id : id of the Jabber connection * return : 0 on success or <0 on error */ int xj_jcon_pool_del(xj_jcon_pool jcp, xj_jkey jkey) { int i = 0; if(jcp==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("removing a connection from the pool\n"); #endif while(i < jcp->len) { if((jcp->ojc[i]!=NULL) && jcp->ojc[i]->jkey->hash==jkey->hash && (!strncmp(jcp->ojc[i]->jkey->id->s,jkey->id->s,jkey->id->len))) { xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; break; } i++; } return 0; }
/** * worker implementation * - jwl : pointer to the workers list * - jaddress : address of the jabber server * - jport : port of the jabber server * - rank : worker's rank * - db_con : connection to database * dbf: database module callbacks structure * #return : 0 on success or <0 on error */ int xj_worker_process(xj_wlist jwl, char* jaddress, int jport, int rank, db_con_t* db_con, db_func_t* dbf) { int pipe, ret, i, pos, maxfd, flag; xj_jcon_pool jcp; struct timeval tmv; fd_set set, mset; xj_sipmsg jsmsg; str sto; xj_jcon jbc = NULL; xj_jconf jcf = NULL; char *p, buff[1024], recv_buff[4096]; int flags, nr, ltime = 0; db_key_t keys[] = {"sip_id", "type"}; db_val_t vals[2]; db_key_t col[] = {"jab_id", "jab_passwd"}; db_res_t* res = NULL; vals[0].type=DB_STRING; vals[0].nul=0; vals[0].val.string_val=buff; vals[1].type=DB_INT; vals[1].nul=0; vals[1].val.int_val=0; _xj_pid = getpid(); //signal(SIGTERM, xj_sig_handler); //signal(SIGINT, xj_sig_handler); //signal(SIGQUIT, xj_sig_handler); signal(SIGSEGV, xj_sig_handler); if(registrar) { jab_gw_name.s = registrar; jab_gw_name.len = strlen(registrar); if(registrar[0]== 's' && registrar[1]== 'i' && registrar[2]== 'p' && registrar[3]== ':') { jab_gw_name.s += 4; jab_gw_name.len -= 4; } } if(!jwl || !jwl->aliases || !jwl->aliases->jdm || !jaddress || rank >= jwl->len) { DBG("XJAB:xj_worker[%d]:%d: exiting - wrong parameters\n", rank, _xj_pid); return -1; } pipe = jwl->workers[rank].rpipe; DBG("XJAB:xj_worker[%d]:%d: started - pipe=<%d> : 1st message delay" " <%d>\n", rank, _xj_pid, pipe, jwl->delayt); if((jcp=xj_jcon_pool_init(jwl->maxj,XJ_MSG_POOL_SIZE,jwl->delayt))==NULL) { DBG("XJAB:xj_worker: cannot allocate the pool\n"); return -1; } maxfd = pipe; tmv.tv_sec = jwl->sleept; tmv.tv_usec = 0; FD_ZERO(&set); FD_SET(pipe, &set); while(main_loop) { mset = set; tmv.tv_sec = (jcp->jmqueue.size == 0)?jwl->sleept:1; #ifdef XJ_EXTRA_DEBUG //DBG("XJAB:xj_worker[%d]:%d: select waiting %ds - queue=%d\n",rank, // _xj_pid, (int)tmv.tv_sec, jcp->jmqueue.size); #endif tmv.tv_usec = 0; ret = select(maxfd+1, &mset, NULL, NULL, &tmv); // check the msg queue xj_worker_check_qmsg(jwl, jcp); if(ret <= 0) goto step_x; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: something is coming\n", _xj_pid); #endif if(!FD_ISSET(pipe, &mset)) goto step_y; if(read(pipe, &jsmsg, sizeof(jsmsg)) < sizeof(jsmsg)) { DBG("XJAB:xj_worker:%d: BROKEN PIPE - exiting\n", _xj_pid); break; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: job <%p> from SER\n", _xj_pid, jsmsg); #endif if(jsmsg == NULL || jsmsg->jkey==NULL || jsmsg->jkey->id==NULL) goto step_w; strncpy(buff, jsmsg->jkey->id->s, jsmsg->jkey->id->len); buff[jsmsg->jkey->id->len] = 0; jbc = xj_jcon_pool_get(jcp, jsmsg->jkey); switch(jsmsg->type) { case XJ_SEND_MESSAGE: if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm) && (!jbc||!xj_jcon_get_jconf(jbc,&jsmsg->to,jwl->aliases->dlm))) { xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, NULL); goto step_w; } break; case XJ_REG_WATCHER: case XJ_JOIN_JCONF: case XJ_GO_ONLINE: break; case XJ_EXIT_JCONF: if(jbc == NULL) goto step_w; // close the conference session here if(jbc->nrjconf <= 0) goto step_w; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) xj_jcon_del_jconf(jbc, &jsmsg->to, jwl->aliases->dlm, XJ_JCMD_UNSUBSCRIBE); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_INF_JCONFEXIT, NULL); goto step_w; case XJ_GO_OFFLINE: if(jbc != NULL) jbc->expire = ltime = -1; goto step_w; case XJ_DEL_WATCHER: default: goto step_w; } if(jbc != NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: connection already exists" " for <%s> ...\n", _xj_pid, buff); #endif xj_jcon_update(jbc, jwl->cachet); goto step_z; } // NO OPEN CONNECTION FOR THIS SIP ID #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: new connection for <%s>.\n", _xj_pid, buff); #endif if(dbf->query(db_con, keys, 0, vals, col, 2, 2, NULL, &res) != 0 || RES_ROW_N(res) <= 0) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: no database result when looking" " for associated Jabber account\n", _xj_pid); #endif xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFORB, NULL); goto step_v; } jbc = xj_jcon_init(jaddress, jport); if(xj_jcon_connect(jbc)) { DBG("XJAB:xj_worker:%d: Cannot connect" " to the Jabber server ...\n", _xj_pid); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOJSRV, NULL); goto step_v; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker: auth to jabber as: [%s] / [xxx]\n", (char*)(ROW_VALUES(RES_ROWS(res))[0].val.string_val)); // (char*)(ROW_VALUES(RES_ROWS(res))[1].val.string_val)); #endif if(xj_jcon_user_auth(jbc, (char*)(ROW_VALUES(RES_ROWS(res))[0].val.string_val), (char*)(ROW_VALUES(RES_ROWS(res))[1].val.string_val), XJAB_RESOURCE) < 0) { DBG("XJAB:xj_worker:%d: Authentication to the Jabber server" " failed ...\n", _xj_pid); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JAUTH, NULL); xj_jcon_free(jbc); goto step_v; } if(xj_jcon_set_attrs(jbc, jsmsg->jkey, jwl->cachet, jwl->delayt) || xj_jcon_pool_add(jcp, jbc)) { DBG("XJAB:xj_worker:%d: Keeping connection to Jabber server" " failed! Not enough memory ...\n", _xj_pid); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFULL, NULL); xj_jcon_free(jbc); goto step_v; } /** add socket descriptor to select */ #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: add connection on <%d> \n", _xj_pid, jbc->sock); #endif if(jbc->sock > maxfd) maxfd = jbc->sock; FD_SET(jbc->sock, &set); xj_jcon_get_roster(jbc); xj_jcon_send_presence(jbc, NULL, NULL, "Online", "9"); /** wait for a while - the worker is tired */ //sleep(3); if ((res != NULL) && (dbf->free_result(db_con,res) < 0)) { DBG("XJAB:xj_worker:%d:Error while freeing" " SQL result - worker terminated\n", _xj_pid); return -1; } else res = NULL; step_z: if(jsmsg->type == XJ_GO_ONLINE) goto step_w; if(jsmsg->type == XJ_REG_WATCHER) { // update or register a presence watcher xj_worker_check_watcher(jwl, jcp, jbc, jsmsg); goto step_w; } flag = 0; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) { if((jcf = xj_jcon_get_jconf(jbc, &jsmsg->to, jwl->aliases->dlm)) != NULL) { if((jsmsg->type == XJ_JOIN_JCONF) && !(jcf->status & XJ_JCONF_READY || jcf->status & XJ_JCONF_WAITING)) { if(!xj_jcon_jconf_presence(jbc,jcf,NULL,"online")) jcf->status = XJ_JCONF_WAITING; else { // unable to join the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_JOINJCONF, &jbc->jkey->flag); goto step_w; } } flag |= XJ_ADDRTR_CON; } else { // unable to get the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NEWJCONF, &jbc->jkey->flag); goto step_w; } } if(jsmsg->type != XJ_SEND_MESSAGE) goto step_w; // here will come only XJ_SEND_MESSAGE switch(xj_jcon_is_ready(jbc,jsmsg->to.s,jsmsg->to.len,jwl->aliases->dlm)) { case 0: #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: SENDING the message to Jabber" " network ...\n", _xj_pid); #endif /*** address correction ***/ sto.s = buff; sto.len = 0; flag |= XJ_ADDRTR_S2J; if(xj_address_translation(&jsmsg->to, &sto, jwl->aliases, flag) == 0) { if(xj_jcon_send_msg(jbc, sto.s, sto.len, jsmsg->msg.s, jsmsg->msg.len, (flag&XJ_ADDRTR_CON)?XJ_JMSG_GROUPCHAT:XJ_JMSG_CHAT)<0) xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); } else DBG("XJAB:xj_worker:%d: ERROR SENDING as Jabber" " message ...\n", _xj_pid); goto step_w; case 1: #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d:SCHEDULING the message.\n", _xj_pid); #endif if(xj_jcon_pool_add_jmsg(jcp, jsmsg, jbc) < 0) { DBG("XJAB:xj_worker:%d: SCHEDULING the message FAILED." " Message was dropped.\n",_xj_pid); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_STOREJMSG, &jbc->jkey->flag); goto step_w; } else // skip freeing the SIP message - now is in queue goto step_y; case 2: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOREGIM, &jbc->jkey->flag); goto step_w; case 3: // not joined to Jabber conference xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, &jbc->jkey->flag); goto step_w; default: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); goto step_w; } step_v: // error connecting to Jabber server // cleaning jab_wlist xj_wlist_del(jwl, jsmsg->jkey, _xj_pid); // cleaning db_query if ((res != NULL) && (dbf->free_result(db_con,res) < 0)) { DBG("XJAB:xj_worker:%d:Error while freeing" " SQL result - worker terminated\n", _xj_pid); return -1; } else res = NULL; step_w: if(jsmsg!=NULL) { xj_sipmsg_free(jsmsg); jsmsg = NULL; } step_y: // check for new message from ... JABBER for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: checking socket <%d>" " ...\n", _xj_pid, jcp->ojc[i]->sock); #endif if(!FD_ISSET(jcp->ojc[i]->sock, &mset)) continue; pos = nr = 0; do { p = recv_buff; if(pos != 0) { while(pos < nr) { *p = recv_buff[pos]; pos++; p++; } *p = 0; /** * flush out the socket - set it to nonblocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && !(flags & O_NONBLOCK)) { /* set NONBLOCK bit to enable non-blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags|O_NONBLOCK); } } if((nr = read(jcp->ojc[i]->sock, p, sizeof(recv_buff)-(p-recv_buff))) == 0 ||(nr < 0 && errno != EAGAIN)) { DBG("XJAB:xj_worker:%d: ERROR -" " connection to jabber lost on socket <%d> ...\n", _xj_pid, jcp->ojc[i]->sock); xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name,XJ_DMSG_ERR_DISCONNECTED,&jbc->jkey->flag); // make sure that will ckeck expired connections ltime = jcp->ojc[i]->expire = -1; FD_CLR(jcp->ojc[i]->sock, &set); goto step_xx; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: received: %dbytes Err:%d/EA:%d\n", _xj_pid, nr, errno, EAGAIN); #endif xj_jcon_update(jcp->ojc[i], jwl->cachet); if(nr>0) p[nr] = 0; nr = strlen(recv_buff); pos = 0; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker: JMSG START ----------\n%.*s\n" " JABBER: JMSGL:%d END ----------\n", nr, recv_buff, nr); #endif } while(xj_manage_jab(recv_buff, nr, &pos, jwl->aliases, jcp->ojc[i]) == 9 && main_loop); /** * flush out the socket - set it back to blocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && (flags & O_NONBLOCK)) { /* reset NONBLOCK bit to enable blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags & ~O_NONBLOCK); } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: msgs from socket <%d> parsed" " ...\n", _xj_pid, jcp->ojc[i]->sock); #endif } // end FOR(i = 0; i < jcp->len; i++) step_x: if(ret < 0) { DBG("XJAB:xj_worker:%d: SIGNAL received!!!!!!!!\n", _xj_pid); maxfd = pipe; FD_ZERO(&set); FD_SET(pipe, &set); for(i = 0; i < jcp->len; i++) { if(jcp->ojc[i] != NULL) { FD_SET(jcp->ojc[i]->sock, &set); if( jcp->ojc[i]->sock > maxfd ) maxfd = jcp->ojc[i]->sock; } } } step_xx: if(ltime < 0 || ltime + jwl->sleept <= get_ticks()) { ltime = get_ticks(); #ifdef XJ_EXTRA_DEBUG //DBG("XJAB:xj_worker:%d: scanning for expired connection\n", // _xj_pid); #endif xj_worker_check_jcons(jwl, jcp, ltime, &set); } } // END while DBG("XJAB:xj_worker:%d: cleaning procedure\n", _xj_pid); return 0; } // end xj_worker_process