/* this is called when a new server is introduced, we just care for the first server when services connect to the hub so that we create the bots and join them to their channels */ void ev_bs_new_server(IRC_Server* nserver, IRC_Server *from) { static int already_loaded = 0; MYSQL_RES *res; MYSQL_ROW row; if (already_loaded) return; res = sql_query("SELECT nick,username,publichost,realname,bid FROM botserv"); while((row = sql_next_row(res))) { MYSQL_RES *res_chan; MYSQL_ROW row_chan; IRC_User *user; u_int32_t bid = atoi(row[4]); user = irc_CreateLocalUser(row[0], row[1], row[2], row[2], row[3], "+r"); mod_do_event(e_bot_introduce, &bid, NULL); irc_AddUMsgEvent(user, "\1PING", bs_ctcp_ping); /* CTCP PING reply */ res_chan = sql_query("SELECT c.name FROM chanserv c, botserv_chans bc " "WHERE bc.bid=%d AND c.scid=bc.scid", bid); while((row_chan = sql_next_row(res_chan))) { IRC_Chan *chan = irc_ChanJoin(user, row_chan[0], 0); irc_ChanMode(bsu.u, chan, "+ao %s %s", user->nick, user->nick); } sql_free(res_chan); } sql_free(res); already_loaded = -1; }
static void ns_blist_list(IRC_User *s, IRC_User *u) { MYSQL_RES* res; MYSQL_ROW row; char sql[1024]; int count = 0; char *c; char* mask; mask = strtok(NULL, " "); if(IsNull(mask)) { send_lang(u, s, NS_BLIST_SYNTAX); } else { while((c=strchr(mask, '*'))) *c='%'; snprintf(sql, sizeof(sql), "SELECT data FROM ns_blist WHERE data LIKE %s", sql_str(mask)); res = sql_query("%s", sql); send_lang(u, s, NS_BLIST_HEADER); while((row = sql_next_row(res))) { send_lang(u, s, NS_BLIST_ITEM_X, row[0]); count++; } send_lang(u, s, NS_BLIST_TAIL, count); sql_free(res); } }
/** load code **/ int mod_load(void) { int i; MYSQL_RES* res; MYSQL_ROW row; csu = chanserv_suser(); /* alocate memory to keep the chan list*/ last_reg_list = malloc(DisplayCount * sizeof(char*)); res = sql_query("SELECT name FROM chanserv " "WHERE (flags & %d)=0 ORDER BY t_reg DESC " "LIMIT %d", CFL_PRIVATE, DisplayCount); i = 0; while((row = sql_next_row(res)) && i < DisplayCount) { last_reg_list[i++] = strdup(row[0]); } sql_free(res); while(i < DisplayCount) /* just clear the remaining free items */ last_reg_list[i++] = NULL; /* Add event to dump list */ irc_AddEvent(ET_NEW_USER, ev_cs_lastreg_new_user); /* new user */ /* Add action to update list */ mod_add_event_action(e_chan_register, (ActionHandler) ev_cs_lastreg_chan_register); return 0; }
/* Remove a single entry from the hash table given a pointer to that * element and a hash on the element's key. */ static void removeElementGivenHash(Hash * pH, /* The pH containing "elem" */ HashElem * elem, /* The element to be removed from the pH */ unsigned int h /* Hash value for the element */ ) { struct _ht *pEntry; if (elem->prev) { elem->prev->next = elem->next; } else { pH->first = elem->next; } if (elem->next) { elem->next->prev = elem->prev; } if (pH->ht) { pEntry = &pH->ht[h]; if (pEntry->chain == elem) { pEntry->chain = elem->next; } pEntry->count--; assert(pEntry->count >= 0); } free(elem->pKey); sql_free(elem); pH->count--; if (pH->count == 0) { assert(pH->first == 0); assert(pH->count == 0); sqlHashClear(pH); } }
neo_main() { int fd; int ret; int i; int m; r_expr_t *np, *nnp; char *wcp; char wbuf[256]; char sql[256]; extern int sqldebug; sqldebug = 1; sql_lex_init(); /***** for( i = 0; i < sizeof(sql)/sizeof(sql[0]); i++ ) { ******/ for( i = 0; i < 10; i++ ) { printf("sql>"); wcp = gets(wbuf); if( wcp == 0 ) { printf("\n Read Error\n"); break; } printf("\nINPUT:%s\n", wbuf); sql[0] = '\0'; strcpy(sql,wbuf); printf("**** INPUT: HEX DUMP ***\n"); for(m=0;m<256;m++) { if( sql[m] == '\0' ) break; printf("%c(%0x) ",sql[m],sql[m]); } printf("\n**** HEX DUMP ***\n"); sql_mem_init(); /*** sql_lex_start( sql[i], buff, sizeof(buff) ); ***/ sql_lex_start( sql, buff, sizeof(buff) ); ret = sqlparse(); /*** printf("%d:sqlparse=%d[%s]\n", i, ret, sql[i] ); ***/ printf("%d:sqlparse=%d[%s]\n", i, ret, sql ); if( ret ) break; sqlprint( SQLreturn, 0 ); sql_free( SQLreturn ); } }
/* Remove all entries from a hash table. Reclaim all memory. * Call this routine to delete a hash table or to reset a hash table * to the empty state. */ void sqlHashClear(Hash * pH) { HashElem *elem; /* For looping over all elements of the table */ assert(pH != 0); elem = pH->first; pH->first = 0; sql_free(pH->ht); pH->ht = 0; pH->htsize = 0; while (elem) { HashElem *next_elem = elem->next; free(elem->pKey); sql_free(elem); elem = next_elem; } pH->count = 0; }
void sql_closeFree(int cid) { const short *exc = exclist; sql_close(cid); if(!rv_lastStatus) { exclist = exc; sql_free(cid); } } /* sql_closeFree */
int chans_count(u_int32_t snid) { MYSQL_RES* res; MYSQL_ROW row; int count = 0; res = sql_query("SELECT COUNT(*) FROM chanserv WHERE founder=%d", snid); row = sql_next_row(res); if(row) count = atoi(row[0]); sql_free(res); return count; }
/************************************************************************************************** CHECK_XFER If the "xfer" column exists in the soa table, it should contain a list of wildcards separated by commas. In order for this zone transfer to continue, one of the wildcards must match the client's IP address. **************************************************************************************************/ static void check_xfer(TASK *t, MYDNS_SOA *soa) { SQL_RES *res = NULL; SQL_ROW row = NULL; char ip[256]; char *query = NULL; size_t querylen = 0; int ok = 0; memset(&ip, 0, sizeof(ip)); if (!mydns_soa_use_xfer) return; strncpy(ip, clientaddr(t), sizeof(ip)-1); querylen = sql_build_query(&query, "SELECT xfer FROM %s WHERE id=%u%s%s%s;", mydns_soa_table_name, soa->id, (mydns_rr_use_active)? " AND active='" : "", (mydns_rr_use_active)? mydns_rr_active_types[0] : "", (mydns_rr_use_active)? "'" : ""); res = sql_query(sql, query, querylen); RELEASE(query); if (!res) { ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone transfer access rules")); } if ((row = sql_getrow(res, NULL))) { char *wild = NULL, *r = NULL; for (r = row[0]; !ok && (wild = strsep(&r, ",")); ) { if (strchr(wild, '/')) { if (t->family == AF_INET) ok = in_cidr(wild, t->addr4.sin_addr); } else if (wildcard_match(wild, ip)) ok = 1; } } sql_free(res); if (!ok) { dnserror(t, DNS_RCODE_REFUSED, ERR_NO_AXFR); axfr_reply(t); axfr_error(t, _("access denied")); } }
/** internal functions implementation starts here **/ int ev_ns_photo_nick_info(IRC_User* user, u_int32_t* snid) { MYSQL_RES* res; MYSQL_ROW row; char buf[128]; res = sql_query("SELECT id FROM ns_photo WHERE snid=%d AND status='P'", *snid); if((row = sql_next_row(res))) { snprintf(buf, sizeof(buf), "%sview_photo.php?id=%d", BaseURL, atoi(row[0])); send_lang(user, nsu->u, PHOTO_URL_X, buf); } sql_free(res); return 0; }
/* this is called when a new server is introduced */ void ev_os_new_server(IRC_Server* nserver, IRC_Server *from) { static int already_loaded = 0; MYSQL_RES* res; MYSQL_ROW row; if(already_loaded) return; res = sql_query("SELECT letter, mask, message FROM os_sline"); while((row = sql_next_row(res))) irc_SendRaw(NULL, "S%cLINE %s :%s", *row[0], row[1], row[2]); sql_free(res); already_loaded = -1; }
/* checks if a given email is forbidden Returns: 0 not forbidden !=0 is forbidden */ int forbidden_email(char *email) { MYSQL_RES *res; MYSQL_ROW row; char *domain; int i; domain = strchr(email, '@'); if(domain == NULL) return 0; res = sql_query("SELECT count(*) from ns_blist WHERE data=%s OR data=%s", sql_str(email), sql_str(domain)); if(res == NULL || ((row = sql_next_row(res)) == NULL)) return 0; i = atoi(row[0]); sql_free(res); return i; }
/* change_item * Change a dbconf item, the value is validated accoring to the item type * Returns: * 0 - Change was successfull * -1 - Item was not found * -2 - Item of type SWITCH but value is not ON/OFF * -3 - Item of type TIME but value is not a time * -4 - Item of type WORD but value is not a word * -5 - Item of type INT but value is not an positive integer * -6 - Unable to unset item, is not optional */ static int change_item(char *item, char *value) { MYSQL_RES *res; MYSQL_ROW row; int error = 0; res = sql_query("SELECT module,name,stype,optional FROM dbconf WHERE " "CONCAT(module,'.', name) = %s", sql_str(item)); if(!res || !(row = sql_next_row(res))) error = -1; else if((value == NULL) && (*row[3]=='n')) /* this item is not optional */ error = -6; else if(strcasecmp(row[2],"switch") == 0) /* "SWITCH" item */ { if(strcasecmp(value,"on") && strcasecmp(value,"off")) error = -2; } else if(strcasecmp(row[2], "time") == 0) /* "TIME" item */ { if(ftime_str(value) == -1) error = -3; } else if((strcasecmp(row[2], "word") == 0) && value) /* "WORD" item */ { if(strchr(value, ' ')) error = -4; } else if(strcasecmp(row[2], "int") == 0) /* "INT" item */ { if(!is_posint(value)) error = -5; } sql_free(res); if(error) return error; if(sql_execute("UPDATE dbconf SET value=%s " "WHERE CONCAT(module,'.',name)=%s", sql_str(value), sql_str(item)) < 0) return -6; return 0; }
/* Insert an element into the hash table pH. The key is pKey * and the data is "data". * * If no element exists with a matching key, then a new * element is created and NULL is returned. * * If another element already exists with the same key, then the * new data replaces the old data and the old data is returned. * The key is not copied in this instance. If a malloc fails, then * the new data is returned and the hash table is unchanged. * * If the "data" parameter to this function is NULL, then the * element corresponding to "key" is removed from the hash table. */ void * sqlHashInsert(Hash * pH, const char *pKey, void *data) { unsigned int h; /* the hash of the key modulo hash table size */ HashElem *elem; /* Used to loop thru the element list */ HashElem *new_elem; /* New element added to the pH */ assert(pH != 0); assert(pKey != 0); elem = findElementWithHash(pH, pKey, &h); if (elem) { void *old_data = elem->data; if (data == 0) { removeElementGivenHash(pH, elem, h); } else { elem->data = data; assert(elem->pKey != NULL); assert(strcmp(elem->pKey, pKey) == 0); } return old_data; } if (data == 0) return 0; new_elem = (HashElem *) sqlMalloc(sizeof(HashElem)); if (new_elem == 0) return data; new_elem->pKey = strdup(pKey); if (new_elem->pKey == NULL) { sql_free(new_elem); return data; } new_elem->data = data; pH->count++; if (pH->count >= 10 && pH->count > 2 * pH->htsize) { if (rehash(pH, pH->count * 2)) { assert(pH->htsize > 0); h = strHash(pKey) % pH->htsize; } } insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem); return 0; }
void mod_unload(void) { MYSQL_RES *res; MYSQL_ROW row; /* remote pseudo users before disconnecting BotServ */ res = sql_query("SELECT nick FROM botserv"); while((row = sql_next_row(res))) { IRC_User *user = irc_FindUser(row[0]); if(user) irc_QuitLocalUser(user, "Removing service"); } sql_free(res); /* remove botserv and all associated events */ irc_QuitLocalUser(bsu.u, "Removing service"); }
/* Resize the hash table so that it cantains "new_size" buckets. * * The hash table might fail to resize if sql_malloc() fails or * if the new size is the same as the prior size. * Return TRUE if the resize occurs and false if not. */ static int rehash(Hash * pH, unsigned int new_size) { struct _ht *new_ht; /* The new hash table */ HashElem *elem, *next_elem; /* For looping over existing elements */ #if SQL_MALLOC_SOFT_LIMIT>0 if (new_size * sizeof(struct _ht) > SQL_MALLOC_SOFT_LIMIT) { new_size = SQL_MALLOC_SOFT_LIMIT / sizeof(struct _ht); } if (new_size == pH->htsize) return 0; #endif /* The inability to allocates space for a larger hash table is * a performance hit but it is not a fatal error. So mark the * allocation as a benign. Use sqlMalloc()/memset(0) instead of * sqlMallocZero() to make the allocation, as sqlMallocZero() * only zeroes the requested number of bytes whereas this module will * use the actual amount of space allocated for the hash table (which * may be larger than the requested amount). */ sqlBeginBenignMalloc(); new_ht = (struct _ht *)sqlMalloc(new_size * sizeof(struct _ht)); sqlEndBenignMalloc(); if (new_ht == 0) return 0; sql_free(pH->ht); pH->ht = new_ht; pH->htsize = new_size = sqlMallocSize(new_ht) / sizeof(struct _ht); memset(new_ht, 0, new_size * sizeof(struct _ht)); for (elem = pH->first, pH->first = 0; elem; elem = next_elem) { unsigned int h = strHash(elem->pKey) % new_size; next_elem = elem->next; insertElement(pH, &new_ht[h], elem); } return 1; }
static taskexec_t ixfr_purge_all_soas(TASK *t, void *data) { /* * Retrieve all zone id's that have deleted records. * * For each zone get the expire field and delete any records that have expired. * */ SQL_RES *res = NULL; SQL_ROW row = NULL; size_t querylen; const char *QUERY0 = "SELECT DISTINCT zone FROM %s WHERE active='%s'"; const char *QUERY1 = "SELECT origin FROM %s " "WHERE id=%u;"; const char *QUERY2 = "DELETE FROM %s WHERE zone=%u AND active='%s' " " AND stamp < DATE_SUB(NOW(),INTERVAL %u SECOND);"; char *query = NULL; /* * Reset task timeout clock to some suitable value in the future */ t->timeout = current_time + ixfr_gc_interval; /* Try again e.g. tomorrow */ querylen = sql_build_query(&query, QUERY0, mydns_rr_table_name, mydns_rr_active_types[2]); if (!(res = sql_query(sql, query, querylen))) ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone id's for DELETED records")); RELEASE(query); while((row = sql_getrow(res, NULL))) { unsigned int id = atou(row[0]); char *origin = NULL; MYDNS_SOA *soa = NULL; SQL_RES *sres = NULL; querylen = sql_build_query(&query, QUERY1, mydns_soa_table_name, id); if (!(res = sql_query(sql, query, querylen))) ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone from DELETED record zone id")); RELEASE(query); if (!(row = sql_getrow(res, NULL))) { Warnx(_("%s: no soa found for soa id %u"), desctask(t), id); continue; } origin = row[0]; if (mydns_soa_load(sql, &soa, origin) == 0) { querylen = sql_build_query(&query, QUERY2, mydns_rr_table_name, soa->id, mydns_rr_active_types[2], soa->expire); if (sql_nrquery(sql, query, querylen) != 0) WarnSQL(sql, "%s: %s %s", desctask(t), _("error deleting expired records for zone "), soa->origin); RELEASE(query); sql_free(sres); } } sql_free(res); RELEASE(query); return (TASK_CONTINUE); }
/* s = service the command was sent to u = user the command was sent from */ void bs_assign(IRC_User *s, IRC_User *u) { char *channame, *bot_nick; MYSQL_RES *res; MYSQL_ROW row; ChanRecord *cr; IRC_User *user; int bid; channame = strtok(NULL, " "); bot_nick = strtok(NULL, ""); if(IsNull(channame) || *channame=='\0' || IsNull(bot_nick) || *bot_nick=='\0') { send_lang(u, s, BS_ASSIGN_SYNTAX); return; } else { cr = OpenCR(channame); if (!cr) { send_lang(u, s, BS_ASSIGN_NO_SUCH_CHANNEL, channame); return; } bs_group = find_group(AdminRole); if ((u->snid != cr->founder) && !is_sadmin(u->snid) && !is_member_of(u, bs_group)) { send_lang(u, s, BS_ASSIGN_NOT_ALLOWED, channame); return; } res = sql_query("SELECT bid FROM bs_chan WHERE scid=%d", cr->scid); row = sql_next_row(res); if (row) { send_lang(u, s, BS_ASSIGN_ALREADY_ASSIGNED, channame); sql_free(res); return; } sql_free(res); res = sql_query("SELECT bid FROM botserv WHERE nick=%s", sql_str(bot_nick)); row = sql_next_row(res); if (!row) { send_lang(u, s, BS_ASSIGN_NO_SUCH_BOT, bot_nick); sql_free(res); return; } bid = atoi(row[0]); sqlb_init("bs_chan"); sqlb_add_int("scid", cr->scid); sqlb_add_int("bid", bid); sqlb_add_int("kick", 0); sqlb_add_int("ttb", 0); sqlb_add_int("capsmin", 0); sqlb_add_int("capspercent", 0); sqlb_add_int("floodlines", 0); sqlb_add_int("floodsecs", 0); sqlb_add_int("repeattimes", 0); sqlb_add_int("bantype", 0); sqlb_add_int("banlast", 0); sql_execute("%s", sqlb_insert()); res = sql_query("SELECT bid,scid FROM bs_chan WHERE scid=%d AND bid=%d", cr->scid, bid); if (!res) { send_lang(u, s, BS_ASSIGN_FAILED, bot_nick, bid, channame); return; } sql_free(res); user = irc_FindLocalUser(bot_nick); if (user) { IRC_Chan *chan = irc_ChanJoin(user, channame, 0); irc_ChanMode(bsu->u, chan, "+ao %s %s", user->nick, user->nick); } send_lang(u, s, BS_ASSIGN_DONE, bot_nick, channame); return; } return; }
/* s = service the command was sent to u = user the command was sent from */ void bs_unassign(IRC_User *s, IRC_User *u) { char *channame; MYSQL_RES *res; MYSQL_ROW row; ChanRecord *cr; IRC_User *user; int bid; channame = strtok(NULL, ""); if(IsNull(channame) || *channame=='\0') { send_lang(u, s, BS_UNASSIGN_SYNTAX); return; } else { cr = OpenCR(channame); if (!cr) { send_lang(u, s, BS_UNASSIGN_NO_SUCH_CHANNEL, channame); return; } bs_group = find_group(AdminRole); if ((u->snid != cr->founder) && !is_sadmin(u->snid) && !is_member_of(u, bs_group)) { send_lang(u, s, BS_UNASSIGN_NOT_ALLOWED, channame); return; } res = sql_query("SELECT bid FROM bs_chan WHERE scid=%d", cr->scid); row = sql_next_row(res); if (!row) { send_lang(u, s, BS_UNASSIGN_NOT_ASSIGNED, channame); sql_free(res); return; } sql_free(res); bid = atoi(row[0]); res = sql_query("SELECT nick FROM botserv WHERE bid=%d", bid); row = sql_next_row(res); user = irc_FindLocalUser(row[0]); if (user) { IRC_Chan *chan = irc_FindChan(channame); irc_ChanPart(user, chan); } sql_execute("DELETE FROM bs_chan WHERE bid=%d AND scid=%d", bid, cr->scid); send_lang(u, s, BS_UNASSIGN_DONE, user->nick, channame); return; } return; }
/* s = service the command was sent to u = user the command was sent from */ void ns_identify(IRC_User *s, IRC_User *u) { MYSQL_RES *res = NULL; MYSQL_ROW row; char *pass = strtok(NULL, " "); char *extraopt = NULL; if(pass != NULL) extraopt = strtok(NULL, ""); if(IsNull(pass)) send_lang(u, s, IDENTIFY_SYNTAX); else if(u->snid) send_lang(u, s, ALREADY_IDENTIFIED); else if((res = sql_query("SELECT snid, flags, lang, email, vhost" " FROM nickserv WHERE nick=%s", sql_str(irc_lower_nick(u->nick)))) && (row = sql_next_row(res))) { int c = 0; u_int32_t snid = atoi(row[c++]); u_int32_t flags = atoi(row[c++]); int lang = atoi(row[c++]); char *email = row[c++]; char *vhost = row[c++]; if((flags & NFL_SUSPENDED) && sql_singlequery("SELECT reason FROM nickserv_suspensions WHERE snid=%d", snid)) { send_lang(u,s, NICK_X_IS_SUSPENDED_X, u->nick, sql_field(0)); return; } if(check_nick_security(snid, u, pass, email, flags) == -1) { log_log(ns_log, mod_info.name, "Nick %s failed identify by %s", u->nick, irc_UserSMask(u)); if(FailedLoginMax && ++u->fcount>FailedLoginMax) { log_log(ns_log, mod_info.name, "Killing %s on too many failed identify attempts", u->nick); irc_Kill(u, s, "Too many failed identify attempts"); } else send_lang(u, s, INCORRECT_PASSWORD); sql_free(res); return; } else { send_lang(u, s, IDENTIFY_OK); log_log(ns_log, mod_info.name, "Nick %s identified by %s", u->nick, irc_UserSMask(u)); update_nick_online_info(u, snid, lang); if(vhost && irccmp(u->publichost, vhost)) /* we need to set the vhost */ irc_ChgHost(u, vhost); irc_CancelUserTimerEvents(u); /* delete the pending change nick event */ mod_do_event(e_nick_identify, u, &snid); } } else send_lang(u, s, NICK_NOT_REGISTERED); sql_free(res); }
/** internal functions implementation starts here **/ void ms_send(IRC_User *s, IRC_User *u) { u_int32_t source_snid; u_int32_t snid; u_int32_t id; char* target; char* message; int mcount = 0; int maxmemos; int bquota; u_int32_t flags; u_int32_t memo_flags = 0; /* status validation */ CHECK_IF_IDENTIFIED_NICK target = strtok(NULL, " "); message = strtok(NULL, ""); if(target && (snid = nick2snid(target)) == 0) { send_lang(u, s, NICK_X_NOT_REGISTERED, target); return; } /* we need to read memo options first */ memoserv_get_options(snid, &maxmemos, &bquota, &flags); if(flags && MOFL_AUTOSAVE) memo_flags = MFL_SAVED; if(NickSecurityCode && !IsAuthenticated(u)) send_lang(u, s, NEEDS_AUTH_NICK); else /* syntax validation */ if(IsNull(target) || IsNull(message)) send_lang(u, s, SEND_SYNTAX); /* check maxmemos */ else if(flags & MOFL_NOMEMOS) send_lang(u, s, MS_SEND_NOMEMOS); else if((mcount = memos_count(snid)) >= maxmemos) send_lang(u, s, MAX_MEMOS_REACHED_X_X, target, maxmemos); #if 0 /* check buddy quota for non buddies */ else if(is_buddy && (maxmemos-mcount <= bquota) && !is_buddy(snid, source_snid)) send_lang(u, s, MAX_MEMOS_REACHED_X_X, target, maxmemos-bquota); #endif /* execute operation */ else if((id = insert_memo(u->nick, source_snid, snid, message, memo_flags)) > 0) { IRC_User* tu; send_lang(u, s, SENT_MEMO_TO_X, target); tu = irc_FindUser(target); if(tu && tu->snid) /* target is online and identified */ { char memoprev[MEMOPREVMAX+1]; snprintf(memoprev, MEMOPREVMAX, "%s", message); send_lang(tu, s, YOU_GOT_MEMO_FROM_X_X_NUM_X, u->nick, memoprev, id); } if(flags & MOFL_FORWARD) { MYSQL_RES *res; MYSQL_ROW row; res = sql_query("SELECT email, lang FROM nickserv WHERE snid=%d", snid); if(res && (row = sql_next_row(res))) { char* email = row[0]; int lang = atoi(row[1]); email_init_symbols(); email_add_symbol("nick",target); email_add_symbol("email", email); email_add_symbol("message", message); email_add_symbol("subject", lang_str_l(lang, MS_SEND_SUBJECT_X, u->nick)); if(email_send(forward_email) < 0) { log_log(ms_log, mod_info.name, "Error sending forward email to %s by %s", email, irc_UserMask(u)); } } sql_free(res); } } else send_lang(u, s, UPDATE_FAIL); }
/** internal functions implementation starts here **/ void os_sline(IRC_User *s, IRC_User *u) { u_int32_t source_snid; char *cmd; /* status validation */ CHECK_IF_IDENTIFIED_NICK if (!is_soper(u->snid)) { send_lang(u, s, PERMISSION_DENIED); return; } /* syntax validation */ cmd = strtok(NULL, " "); if(IsNull(cmd)) send_lang(u, s, SLINE_SYNTAX); /* sub-command */ else if(strcasecmp(cmd, "ADD") == 0) { u_int32_t id; char* letter = strtok(NULL, " "); char* mask = strtok(NULL, " "); char* message = strtok(NULL, ""); if(letter) letter[0] = toupper(letter[0]); if(letter && strlen(letter)>1) letter[1] = '\0'; if(mask && strlen(mask)>128) letter[128] = '\0'; if(message && strlen(message)>128) message[128] = '\0'; /* syntax validation */ if(IsNull(letter) || IsNull(mask) || IsNull(message)) send_lang(u, s, SLINE_SYNTAX); /* avoid duplicates */ else if(find_sline(*letter, mask) > 0) send_lang(u, s, SLINE_ALREADY_EXISTS_X_X, *letter, mask); /* execute operation */ else if((id = insert_sline(u->nick ,*letter, mask, message)) != 0) { send_lang(u, s, ADDED_SLINE_X_X, *letter, id); irc_SendRaw(NULL, "S%cLINE %s :%s", *letter, mask, message); } else send_lang(u, s, UPDATE_FAIL); } /* sub-command */ else if(strcasecmp(cmd, "DEL") == 0) { u_int32_t id = 0; char *strid; strid = strtok(NULL, " "); if(strid) id = atoi(strid); /* syntax validation */ if(IsNull(strid)) send_lang(u, s, SLINE_SYNTAX); else if(sql_singlequery("SELECT id, letter, mask FROM os_sline WHERE id=%d", id) == 0) send_lang(u, s, SLINE_X_NOT_FOUND, id); /* execute operation */ else if(sql_execute("DELETE FROM os_sline WHERE id=%d", id) > 0) { send_lang(u, s, DELETED_SLINE_X, id); irc_SendRaw(NULL, "UNS%cLINE %s", *sql_field(1), sql_field(2)); } else send_lang(u, s, UPDATE_FAIL); } /* sub-command */ else if(strcasecmp(cmd, "LIST") == 0) { MYSQL_RES* res; MYSQL_ROW row; int rowc = 0; char* letter = strtok(NULL, " "); if(letter) res = sql_query("SELECT " "id, letter, mask, message, who_nick, t_create FROM os_sline" " WHERE letter=%s", sql_str(letter)); else res = sql_query("SELECT id, letter, mask, message, who_nick, t_create FROM os_sline"); if(res) rowc = mysql_num_rows(res); send_lang(u, s, SLINE_LIST_HEADER_X, rowc); while((row = sql_next_row(res))) { send_lang(u, s, SLINE_LIST_FORMAT, atoi(row[0]), *row[1], row[2], row[3], row[4], row[5]); } send_lang(u, s, SLINE_LIST_TAIL); sql_free(res); } else send_lang(u, s, SLINE_SYNTAX); }
int dbconf_cmd_line(int argc, char **argv) { const char* usage = "Usage:\n" "ircsvs conf list [pattern]\n" "ircsvs conf export [pattern]\n" "ircsvs conf set module.setting value\n" "ircsvs conf unset module.setting\n"; char* cmd; char buf[128]; if(argc<1) { printf("%s", usage); return -1; } cmd = argv[0]; if((strcasecmp(cmd, "list") == 0) || (strcasecmp(cmd, "export") == 0)) { MYSQL_RES *res; MYSQL_ROW row; char *where; int is_export = (strcasecmp(cmd, "export") == 0); if(argc>1) { char buf2[128]; snprintf(buf2, sizeof(buf2), "%%%s%%", argv[1]); snprintf(buf, sizeof(buf), " WHERE CONCAT(module,'.',name) LIKE %s", sql_str(buf2)); where = buf; } else where = ""; res = sql_query("SELECT module, name, value, ddesc, stype, optional" " FROM dbconf %s", where); printf("####### Configuration list #######\n"); while((row = sql_next_row(res))) { char *line; line = row[3]; /* show each line from the ddesc field prefixed with # */ while(line) { char *p = line; char *c = strchr(line,'\n'); if(c) { *c = '\0'; line = c+1; } else line = NULL; printf("# %s\n", p); } if(strcmp(row[4],"switch") == 0) printf("# This is a switch option, possible values are On or Off\n"); else if(strcmp(row[4],"time") == 0) printf("# Time value [m=minutes;h=hours;d=days;M=months,Y=years]\n"); else if(*row[5] == 'y') printf("# This setting is optional, you can unset to disable\n"); if(is_export) { if(strcmp(row[4],"word") && strcmp(row[4],"str")) printf("./ircsvs conf set %s.%s %s", row[0], row[1], row[2] ? row[2] : "NULL"); else if(row[2]) printf("./ircsvs conf set %s.%s \"%s\"", row[0], row[1], row[2]); else printf("./ircsvs conf unset %s.%s", row[0], row[1]); } else { if(strcmp(row[4],"word") && strcmp(row[4],"str")) printf("%s.%s = %s", row[0], row[1], row[2] ? row[2] : "NULL"); else if(row[2]) printf("%s.%s = \"%s\"", row[0], row[1], row[2]); else printf("%s.%s = *NOT SET*", row[0], row[1]); } printf("\n\n"); } printf("##################################\n"); sql_free(res); } else if((strcasecmp(cmd, "set") == 0) || (strcasecmp(cmd, "unset") == 0)) { int r; const char *msg = NULL; int unset = (strcasecmp(cmd, "unset") == 0); if((!unset && (argc < 3)) || !strchr(argv[1], '.')) { printf("%s", usage); return -1; } r = change_item(argv[1], unset ? NULL : argv[2]); switch(r) { case 0: msg = NULL; break; case -1: msg = "There is no item %s !\n"; break; case -2: msg = "Value for %s must be On/Off !\n"; break; case -3: msg = "Value for %s must be a time value !\n"; break; case -4: msg = "Value for %s must be a word !\n"; break; case -5: msg = "Value for %s must be a positive integer !\n"; break; case -6: msg = "Value for %s can't be unset, is not an optional setting!\n"; break; default: msg = "Unknown error changing %s !\n"; break; } /* check for errors */ if(msg) { printf(msg, argv[1]); return r; } if(unset) printf("%s successfully unset\n", argv[1]); else printf("%s successfully changed to: %s\n", argv[1], argv[2]); } return 0; };
/* s = service the command was sent to u = user the command was sent from */ void cs_show(IRC_User *s, IRC_User *u) { MYSQL_RES* res; MYSQL_ROW row; u_int32_t source_snid; int rowc = 0; CHECK_IF_IDENTIFIED_NICK if(NeedsAuth && !IsAuthenticated(u)) send_lang(u, s, NEEDS_AUTH_NICK); else { /* List channels you are founder at */ res = sql_query("SELECT name FROM chanserv WHERE founder=%d", source_snid); row = sql_next_row(res); if(row) { send_lang(u, s, SHOW_FOUNDER_HEADER); do { ++rowc; send_lang(u, s, SHOW_ITEM_X, row[0]); } while((row = sql_next_row(res))); send_lang(u, s, SHOW_TAIL_X, rowc); } else send_lang(u, s, SHOW_FOUNDER_EMPTY); sql_free(res); rowc = 0; /* List channels you are successor at */ res = sql_query("SELECT name FROM chanserv WHERE successor=%d", source_snid); row = sql_next_row(res); if(row) { send_lang(u, s, SHOW_SUCCESSOR_HEADER); do { ++rowc; send_lang(u, s, SHOW_ITEM_X, row[0]); } while((row = sql_next_row(res))); send_lang(u, s, SHOW_TAIL_X, rowc); } sql_free(res); rowc = 0; /* List channels you are successor at */ res = sql_query("SELECT r.name, c.name, cr.message, cr.flags " "FROM cs_role r, chanserv c, cs_role_users cr " "WHERE cr.snid=%d AND r.rid=cr.rid AND c.scid=cr.scid", source_snid); row = sql_next_row(res); if(row) { send_lang(u, s, SHOW_ROLES_HEADER); do { char flagstr[64]; u_int32_t flags = atoi(row[3]); if(flags & CRF_REJECTED) snprintf(flagstr, sizeof(flagstr), " %s", lang_str(u, REJECTED_ROLE)); else if(flags & CRF_PENDING) snprintf(flagstr, sizeof(flagstr), " %s", lang_str(u, PENDING_ROLE)); else flagstr[0] = '\0'; ++rowc; if(row[2]) send_lang(u, s, SHOW_ROLES_ITEM_X_X_X_X, row[0], row[1], flagstr, row[2]); else send_lang(u, s, SHOW_ROLES_ITEM_X_X_X, row[0], row[1], flagstr); } while((row = sql_next_row(res))); send_lang(u, s, SHOW_ROLES_TAIL_X, rowc); } sql_free(res); } }
/* s = service the command was sent to u = user the command was sent from */ void ns_group(IRC_User *s, IRC_User *u) { u_int32_t source_snid; u_int32_t snid; char *cmd; char *gname; char *nick; int memberc = 0; u_int32_t master_sgid; u_int32_t sgid; CHECK_IF_IDENTIFIED_NICK cmd = strtok(NULL, " "); gname = strtok(NULL, " "); /* base syntax validation */ if(IsNull(cmd)) send_lang(u, s, NS_GROUP_SYNTAX); else if(strcasecmp(cmd,"CREATE") == 0) { char *master; char *gdesc; char *umodes = NULL; master = strtok(NULL, " "); gdesc = strtok(NULL, ""); if(gname) /* first check if the name contains umodes */ { char *pumodes; char *eumodes; pumodes = strchr(gname,'['); if(pumodes && pumodes[0]) { *(pumodes++) = '\0'; eumodes = strchr(pumodes,']'); if(eumodes) { *eumodes = '\0'; umodes = pumodes; } } } /* syntax validation */ if(IsNull(gname) || IsNull(master)) send_lang(u, s, NS_GROUP_CREATE_SYNTAX); /* permissions validation */ else if(!is_sroot(source_snid)) send_lang(u, s, NICK_NOT_ROOT); /* check requirements */ else if((master_sgid = find_group(master)) == 0) send_lang(u, s, NS_GROUP_MASTER_NOT_FOUND, master); /* avoid duplicates */ else if((sgid = find_group(gname)) != 0) send_lang(u, s, NS_GROUP_ALREADY_EXISTS, gname); /* execute operation */ else if(group_create(gname, master_sgid, gdesc, umodes) > 0) /* report operation status */ send_lang(u, s, NS_GROUP_CREATE_OK, gname); else send_lang(u, s, UPDATE_FAIL); } else if(strcasecmp(cmd,"ADD") == 0) { u_int32_t duration = 0; time_t master_expire = 0; u_int32_t is_master_sgid; char *duration_str; nick = strtok(NULL, " "); duration_str = strtok(NULL, " "); if(duration_str) duration = time_str(duration_str); /* syntax validation */ if(IsNull(gname) || IsNull(nick)) send_lang(u, s, NS_GROUP_ADD_SYNTAX); /* check requirements */ else if((snid = nick2snid(nick)) == 0) send_lang(u, s, NO_SUCH_NICK_X, nick); else if((sgid = find_group(gname)) == 0) send_lang(u, s, NO_SUCH_GROUP_X, gname); /* privileges validation */ else if(group_is_full(sgid)) send_lang(u, s, NS_GROUP_IS_FULL_X); else if(((is_master_sgid = is_master(source_snid, sgid))== 0) && !is_sroot(source_snid)) send_lang(u, s, NOT_MASTER_OF_X, gname); /* avoid duplicates */ else if(sql_singlequery("SELECT t_expire FROM ns_group_users " " WHERE sgid=%d AND snid=%d", is_master_sgid, source_snid) && (master_expire = sql_field_i(0)) && duration) send_lang(u, s, NS_GROUP_CANT_DEFINE_TIME_X, gname); else if(is_member_of(snid, sgid)) send_lang(u, s, NICK_X_ALREADY_ON_X, nick, gname); /* execute operation */ else { time_t t_expire = 0; if(master_expire) t_expire = master_expire; else if(duration) t_expire = irc_CurrentTime + duration; if(add_to_group(sgid, snid, t_expire) > 0) /* report operation status */ { char *server = strchr(gname, '@'); IRC_User *user = irc_FindUser(nick); send_lang(u, s, NICK_ADDED_X_X, nick, gname); if(server) /* we have a server rule to be validated */ ++server; if(user && (!server || (strcasecmp(server,u->server->sname) == 0))) { if(user->extra[ED_GROUPS] == NULL) { user->extra[ED_GROUPS] = malloc(sizeof(darray)); array_init(user->extra[ED_GROUPS], 1, DA_INT); } array_add_int(user->extra[ED_GROUPS], sgid); } } else send_lang(u, s, UPDATE_FAIL); } } else if(strcasecmp(cmd,"DEL") == 0) { nick = strtok(NULL, " "); /* syntax validation */ if(IsNull(gname) || IsNull(nick)) send_lang(u, s, NS_GROUP_DEL_SYNTAX); /* check requirements */ else if((sgid = find_group(gname)) == 0) send_lang(u, s, NO_SUCH_GROUP_X, gname); else if((snid = nick2snid(nick)) == 0) send_lang(u, s, NO_SUCH_NICK_X, nick); /* privileges validation */ else if(!is_sroot(source_snid) && !is_master(source_snid, sgid)) send_lang(u, s, NOT_MASTER_OF_X, gname); else if(!is_member_of(snid, sgid)) send_lang(u, s, NICK_X_NOT_ON_GROUP_X, nick, gname); /* execute operation */ else if(del_from_group(sgid, snid) > 0) /* report operation status */ { IRC_User *user = irc_FindUser(nick); send_lang(u, s, NICK_DEL_X_X, nick, gname); if(user) array_del_int(user->extra[ED_GROUPS], sgid); } else send_lang(u, s, UPDATE_FAIL); } else if(strcasecmp(cmd,"INFO") == 0) { /* syntax validation */ if(IsNull(gname)) send_lang(u, s, NS_GROUP_INFO_SYNTAX); /* check requirements */ else if((sgid = find_group(gname)) == 0) send_lang(u, s, NO_SUCH_GROUP_X, gname); /* check privileges */ else if(!is_master(source_snid, sgid) && !is_member_of(source_snid, sgid)) send_lang(u, s, NOT_MASTER_OR_MEMBER_X, gname); else if((sgid = find_group(gname))) /* we need to get the group description */ { /* execute operation */ MYSQL_RES* res; master_sgid = 0; sql_singlequery("SELECT gdesc, master_sgid FROM ns_group WHERE sgid=%d", sgid); send_lang(u, s, NS_GROUP_INFO_X, gname); if(sql_field(0)) send_lang(u, s, NS_GROUP_INFO_DESC_X, sql_field(0)); master_sgid = sql_field_i(1); if(master_sgid != 0) { if(sql_singlequery("SELECT name FROM ns_group WHERE sgid=%d", master_sgid) > 0) { send_lang(u, s, NS_GROUP_INFO_MASTER_X, sql_field(0)); } } res = sql_query("SELECT n.nick, gm.t_expire FROM " "nickserv n, ns_group_users gm WHERE gm.sgid=%d AND n.snid=gm.snid", sgid); if(sql_next_row(res) == NULL) send_lang(u, s, NS_GROUP_EMPTY); else { do { char buf[64]; struct tm *tm; time_t t_expire = sql_field_i(1); buf[0] = '\0'; if(t_expire) { tm = localtime(&t_expire); strftime(buf, sizeof(buf), format_str(u, DATE_FORMAT), tm); send_lang(u,s, NS_GROUP_ITEM_X_X, sql_field(0), buf); } else send_lang(u,s, NS_GROUP_ITEM_X, sql_field(0)); ++memberc; } while(sql_next_row(res)); send_lang(u, s, NS_GROUP_MEMBERS_TAIL_X, memberc); } sql_free(res); } } else if(strcasecmp(cmd,"DROP") == 0) { /* syntax validation */ if(IsNull(gname)) send_lang(u, s, NS_GROUP_DROP_SYNTAX); /* privileges validation */ else if(!is_sroot(source_snid)) send_lang(u, s, NICK_NOT_ROOT); /* check requirements */ else if((sgid = find_group(gname)) == 0) send_lang(u, s, NO_SUCH_GROUP_X, gname); /* NOTE: The following sql_field( depends on previous find_group( */ else if(!sql_field(2) || (master_sgid = atoi(sql_field(2))) == 0) send_lang(u, s, CANT_DROP_ROOT); /* execute operation */ else if(drop_group(sgid)>0) /* report operation status */ send_lang(u, s, NS_GROUP_DROPPED_X, gname); else send_lang(u, s, UPDATE_FAIL); } else if(strcasecmp(cmd,"LIST") == 0) /* List groups */ { MYSQL_RES* res; MYSQL_ROW row; /* privileges validation */ if(!is_sroot(source_snid)) send_lang(u, s, NICK_NOT_ROOT); else { res = sql_query("SELECT name, master_sgid, gdesc FROM ns_group"); send_lang(u, s, NS_GROUP_LIST_HEADER); while((row = sql_next_row(res))) { char* mname = ""; if(row[1] && sql_singlequery("SELECT name FROM ns_group WHERE sgid=%d", atoi(row[1])) > 0) mname = sql_field(0); send_lang(u, s, NS_GROUP_LIST_X_X_X, row[0], mname, row[2] ? row[2] : ""); } send_lang(u, s, NS_GROUP_LIST_TAIL); sql_free(res); } } else if(strcasecmp(cmd,"SHOW") == 0) /* Show groups we belong to */ { /* groups count */ int gc = array_count(u->extra[ED_GROUPS]); if(gc == 0) send_lang(u, s, NO_GROUPS); else { MYSQL_RES *res; MYSQL_ROW row; char buf[64]; struct tm *tm; time_t t_expire; #if 0 int i; u_int32_t* data = array_data_int(u->extra[ED_GROUPS]); #endif send_lang(u, s, NS_GROUP_SHOW_HEADER); #if 0 for(i = 0; i < gc; ++i) { if(sql_singlequery("SELECT name,gdesc FROM ns_group WHERE sgid=%d", data[i]) > 0 ) send_lang(u, s, NS_GROUP_SHOW_X_X, sql_field(0), sql_field(1) ? sql_field(1) : ""); } #endif res = sql_query("SELECT g.name, g.gdesc, gu.t_expire FROM ns_group g, ns_group_users gu" " WHERE gu.snid=%d AND g.sgid=gu.sgid ORDER BY g.master_sgid", source_snid); while((row = sql_next_row(res))) { t_expire = sql_field_i(2); buf[0] = '\0'; if(t_expire) { tm = localtime(&t_expire); strftime(buf, sizeof(buf), format_str(u, DATE_FORMAT), tm); send_lang(u,s, NS_GROUP_SHOW_X_X_X, row[0], row[1] ? row[1] : "", buf); } else send_lang(u, s, NS_GROUP_SHOW_X_X, row[0], row[1] ? row[1] : ""); } send_lang(u, s, NS_GROUP_SHOW_TAIL); sql_free(res); } } else if(strcasecmp(cmd,"SET") == 0) { char *option; char *value ; option = strtok(NULL, " "); value = strtok(NULL, " "); /* syntax validation */ if(IsNull(gname) || IsNull(option)) send_lang(u, s, NS_GROUP_SET_SYNTAX); /* privileges validation */ else if(!is_sroot(source_snid)) send_lang(u, s, NICK_NOT_ROOT); /* check requirements */ else if((sgid = find_group(gname)) == 0) send_lang(u, s, NO_SUCH_GROUP_X, gname); else { if(strcasecmp(option,"AUTOMODES") == 0) STRING_SET("autoumodes", AUTOMODES_X_UNSET, AUTOMODES_X_CHANGED_TO_X) else if(strcasecmp(option,"DESC") == 0) STRING_SET("gdesc", DESC_X_UNSET, DESC_X_CHANGED_TO_X) else if(strcasecmp(option, "MAXUSERS") == 0) INT_SET("maxusers", NS_GROUP_SET_MAXUSERS_SET_X_X) else send_lang(u, s, SET_INVALID_OPTION_X, option); } }
/* s = service the command was sent to u = user the command was sent from */ void cs_info(IRC_User *s, IRC_User *u) { ChanRecord* cr; char buf[64]; struct tm *tm; char *target = strtok(NULL, " "); IRC_Chan *chan; u_int32_t source_snid = u->snid; if(NeedsAuth && !IsAuthenticated(u)) send_lang(u, s, NEEDS_AUTH_NICK); else if(IsNull(target)) send_lang(u, s, CHAN_INFO_SYNTAX); else if((cr = OpenCR(target)) == NULL) send_lang(u, s, CHAN_X_NOT_REGISTERED, target); else /* everything is valid */ { int sadmin = (is_soper(source_snid) != 0); send_lang(u, s, CHAN_INFO_HEADER); if(sadmin) send_lang(u, s, CHAN_SCID, cr->scid); send_lang(u, s, CHAN_NAME, target); if(IsPrivateChan(cr) && !sadmin && ((source_snid == 0) || (source_snid && source_snid != cr->founder))) send_lang(u, s, CHAN_INFO_PRIVATE, target); else { if(cr->cdesc) send_lang(u, s, CHAN_DESC, cr->cdesc); if(sql_singlequery("SELECT nick FROM nickserv WHERE snid=%d", cr->founder)) send_lang(u, s, CHAN_FOUNDER_X, sql_field(0)); if(sql_singlequery("SELECT nick FROM nickserv WHERE snid=%d", cr->successor)) send_lang(u, s, CHAN_SUCCESSOR_X, sql_field(0)); tm = localtime(&cr->t_reg); strftime(buf, sizeof(buf), format_str(u, INFO_DATE_FORMAT), tm); send_lang(u, s, CHAN_REGDATE_X_X, buf, ago_time(cr->t_reg, u )); tm = localtime(&cr->t_last_use); strftime(buf, sizeof(buf), format_str(u, INFO_DATE_FORMAT), tm); send_lang(u, s, CHAN_LAST_USE_X_X, buf, ago_time(cr->t_last_use, u )); if(!IsNull(cr->email)) send_lang(u, s, CHAN_EMAIL, cr->email); if(!IsNull(cr->url)) send_lang(u, s, CHAN_URL, cr->url); if(!IsNull(cr->entrymsg)) send_lang(u, s, CHAN_ENTRYMSG, cr->entrymsg); if(!IsNull(cr->last_topic)) send_lang(u, s, CHAN_TOPIC_X, cr->last_topic); if(!IsNull(cr->last_topic_setter)) send_lang(u, s, CHAN_TOPIC_SETTER_X, cr->last_topic_setter); chan = irc_FindChan(target); if(chan && chan->users_count) send_lang(u, s, CHAN_CURRUSERS_X, chan ? chan->users_count : 0); tm = localtime(&cr->t_maxusers); strftime(buf, sizeof(buf), format_str(u, INFO_DATE_FORMAT), tm); send_lang(u, s, CHAN_USERS_REC_X_X_X, cr->maxusers, buf, ago_time(cr->t_maxusers, u)); if((sadmin || (source_snid == cr->founder)) && cr->mlock && cr->mlock[0]) send_lang(u, s, CHAN_MLOCK_X, cr->mlock); if(cr->flags && (sadmin || (source_snid == cr->founder))) send_lang(u, s, CHAN_OPTIONS_X, mask_string(options_mask, cr->flags)); if(cr->flags & NFL_SUSPENDED) { MYSQL_RES *res; MYSQL_ROW row; res = sql_query("SELECT t_when, duration, reason " "FROM chanserv_suspensions WHERE scid=%d", cr->scid); if(res && (row = sql_next_row(res))) { time_t t_when = atoi(row[0]); int remaining = -1; if(atoi(row[1]) != 0) { remaining = atoi(row[1]) - (irc_CurrentTime - t_when); remaining /= 24*3600; remaining++; if(remaining < 0) remaining = 0; } tm = localtime(&t_when); strftime(buf, sizeof(buf), format_str(u, DATE_FORMAT), tm); if(remaining == -1) send_lang(u,s, CS_INFO_SUSPENDED_X_FOREVER, buf); else send_lang(u,s, CS_INFO_SUSPENDED_X_X, buf, remaining); send_lang(u,s, CS_INFO_SUSPENDED_REASON, row[2]); } sql_free(res); } send_lang(u, s, CHAN_INFO_TAIL); } CloseCR(cr); } }