#include <iostream> #include <mysql/mysql.h> #include "merc.h" #include "channel.h" int lastChan = 1; extern MYSQL *db; std::list<Channel *> chanList; Command chanCmdTable[] = { { "new", &Channel::New, true }, { "history", &Channel::History, false }, { "ignore", &Channel::Ignore, true }, { "acknowledge",&Channel::Acknowledge, true }, { "on", &Channel::On, false }, { "off", &Channel::Off, false }, { "lock", &Channel::Lock, false }, { NULL, NULL } }; Channel::Channel(char *chanName): name(NULL), bit(++lastChan), lastTID(0) { name = str_dup(capitalize(chanName) ); } Channel::~Channel( ) { Topic *tpc; std::list<Topic *>::iterator i; free_string(name); for( i = topics.begin(); i != topics.end(); ) { tpc = *i; ++i; delete tpc; } } void Channel::Send(ACCOUNT *pAcnt, std::string &txt) { std::string str; char buf[MSL]; sprintf(buf, "{r[{W%s{r]{W ", name); str += buf; str += txt; str += "\n\r{x"; ptc(pAcnt, (char *)str.c_str()); } Topic * Channel::getTopic( int id ) { std::list<Topic *>::iterator i; for( i = topics.begin() ; i != topics.end() ; ++i ) if( (*i)->id == id ) return (*i); return NULL; } void Channel::interpret(ACCOUNT *pAcnt, char *argument ) { Post *post; char arg[MSL]; char orig[MSL]; orig[0] = '\0'; strcpy(orig, argument); arg[0] = '\0'; argument = one_argument(argument,arg); if(arg[0] == '\0') { ptc(pAcnt, "{DUse this command with {r'{W!{r'{D as your argument to view channel functions.\n\r"); ptc(pAcnt, "To speak, use the syntax{r: '<{Wtopic id{r>{W:{r<{Wpost id{r> <{Wmessage{r>'{D\n\r"); ptc(pAcnt, "If you don't supply a topic id, and/or a post id, it will simply\n\rpost on the last topic you spoke on, and point it to the last post on the topic.\n\r"); if(pAcnt->lastTID[bit] == 0 ) ptc(pAcnt, "You have yet to speak on a topic, so not supplying a TID will give you an error message.\n\r"); else { ptc(pAcnt, "If a topic id and post id are not supplied, you will post to{r: "); ptc(pAcnt, "{r[{W%s{r][{DT{r:{W%d{r][{DP{r:{W%d{r]{D\n\r", name, pAcnt->lastTID[bit], getTopic(pAcnt->lastTID[bit])->lastPID); } return; } if(IS_SET(pAcnt->common_flags, COMMON_BANNED) ) { HandleBanned(pAcnt, argument); return; } if(orig[0] == '!') { if(!CheckChanFunc(pAcnt, &orig[1]) ) { int wrap=0; ptc(pAcnt, "{DChannel function not found, try:\n\r\n\r\t"); for(int i = 0; chanCmdTable[i].name != NULL ; ++i ) { ptc(pAcnt, "{W%-13s{D", chanCmdTable[i].name); if( (++wrap % 3 ) == 0 ) ptc(pAcnt, "\n\r\t"); } if(wrap%3) ptc(pAcnt, "\n\r"); return; } return; } int tid = -1, pid = -1; char ctid[MSL], cpid[MSL]; bool useOrig = false, tSuc = false, pSuc = false; ctid[0] = '\0'; cpid[0] = '\0'; Topic *topic = NULL; Post *reply; seperateArgs(arg, ctid, cpid); if( !is_number(ctid) ) { if(pAcnt->lastTID[bit] == 0 ) { ptc(pAcnt, "Sorry, unable to grab a default topic ID.\n\r"); return; } tid = pAcnt->lastTID[bit]; tSuc = true; } else tid = atoi(ctid); if( !is_number(cpid) ) { pSuc = true; pid = -2; } else pid = atoi(cpid); if(pSuc && tSuc) useOrig = true; if(tid == -1 || pid == -1 ) { ptc(pAcnt, "Please use numbers for the ID's.\n\r"); return; } if( !(topic = getTopic(tid) ) ) { ptc(pAcnt, "Topic %d does not exist\n\r", tid); return; } else if( pid == -2 ) pid = topic->lastPID; if(pSuc && argument[0] == '!' && !str_prefix( argument, "!history") ) { topic = getTopic(tid); ptc(pAcnt, "%s\n\r", argument); topic->PostHist(pAcnt, one_argument(argument,arg)); return; } if(topic->locked) { ptc(pAcnt, "Sorry, you can't post on a locked topic.\n\r"); return; } if(pAcnt->isIgnoring(topic, bit) ) { ptc(pAcnt, "Sorry, you can't post on topics you're ignoring.\n\r"); return; } if( !(reply = topic->getPost(pid) ) ) { ptc(pAcnt, "Post %d does not exist\n\r",pid); return; } if(!useOrig && argument[0] == '\0' ) { ptc(pAcnt, "Say what?\n\r"); return; } post = new Post(reply); post->msg = str_dup(useOrig ? orig : argument); post->poster = str_dup(pAcnt->name); post->Send(); pAcnt->lastTID[bit] = tid; return; } //They are so to stupid to realize that they're //Posting to no one if they're banned. lol. I hope! void Channel::HandleBanned(ACCOUNT *pAcnt, char *arg ) { ptc(pAcnt, "{r[{W%s{r]{W{r[{WT{D:{W1{r][{WP{D:{W1{r]{D %s{r:{W%s", name, pAcnt->name,arg); return; } void Channel::LoadTopics() { MYSQL_ROW row; MYSQL_RES *res; char query[MSL]; int maxTopic; sprintf(query, "SELECT * FROM Topics WHERE chanId=%d", bit ); if( mysql_real_query(db, query, strlen(query) ) ) { logfp(LOG_BUG, "Channel::LoadTopics: %s", mysql_error(db) ); return; } if( !(res = mysql_store_result(db) ) ) { logfp(LOG_BUG, "Channel::LoadTopics: Result null!"); return; } if( (maxTopic = mysql_num_rows(res) ) <= 0 ) { logfp(LOG_BUG, "Channel::LoadTopics: No rows!" ); mysql_free_result(res); return; } for( int i = 0; i < maxTopic ; ++i ) { row = mysql_fetch_row(res); Topic *topic; topic = new Topic(this); topic->name = str_dup(row[0]); topic->subject = str_dup(row[1]); topic->id = atoi(row[2]); topic->lastPID = atoi(row[4]); topic->LoadPosts(); } mysql_free_result(res); return; } void Channel::TopicHist( ACCOUNT *pAcnt, char *argument ) { int page=10; std::list<Topic *>::iterator i; if(argument[0] != '\0' && is_number(argument) ) page = atoi(argument); if(page <= 0) page = 1; if(page != 1) ptc(pAcnt, "These are the last %d topics made on %s\n\r", page, name); else ptc(pAcnt, "This is the last topic made on %s\n\r", name); for(i = topics.begin() ; i != topics.end() && page > 0 ; ++i, --page) { if((*i)->locked) ptc(pAcnt, "{r({WLocked{r)"); ptc(pAcnt,"{r[{W%s{r]{W{r[{WT{D:{W%d{r]{W {D%s{r:{W%s{x\n\r", name, (*i)->id, (*i)->name,(*i)->subject); } } void Channel::New(ACCOUNT *pAcnt, char* argument) { char msg[MSL], subject[MSL], *sub, *txt; msg[0] = '\0'; subject[0] = '\0'; if(argument[0] == '\0' ) { ptc(pAcnt, "{DSyntax{r: {W!new {r<{Wsubject{r>{D:{r<{WContents of first post{r>\n\r"); return; } seperateArgs(argument, subject, msg); sub = subject; txt = msg; while(isspace(*sub) ) ++sub; while(isspace(*txt) ) ++txt; if(*txt == '\0' || *sub == '\0' ) { ptc(pAcnt,"You must provide both a subject and content for the first post.\n\r"); return; } ptc(pAcnt, "%s:%s\n\r",txt,sub); Topic *topic = new Topic(this); Post *post; topic->name = str_dup(pAcnt->name); topic->subject = str_dup(sub); topic->Send(); post = new Post(topic); post->poster = str_dup(pAcnt->name); post->msg = str_dup(txt); post->Send(); pAcnt->lastTID[bit] = topic->id; return; } void Channel::History(ACCOUNT *pAcnt, char* argument) { TopicHist(pAcnt, argument); return; } void Channel::Ignore(ACCOUNT *pAcnt, char* argument) { if(argument[0] == '\0') { ptc(pAcnt, "{DSyntax{r: {W!ignore {r<{Wtopic id #{r>{x\n\r"); ptc(pAcnt, " list\n\r"); return; } if(!is_number(argument ) ) { if(!strcasecmp(argument, "list") ) pAcnt->showIgnore(this); else ptc(pAcnt, "You must use the topics ID to ignore it, or use list as the argument to see all ignored.\n\r"); return; } int tid = atoi(argument); Topic *topic; if( !(topic = getTopic(tid) ) ) { ptc(pAcnt, "You can only ignore topics that exist.\n\r"); return; } pAcnt->addIgnore(topic, bit); ptc(pAcnt, "Will now ignore all posts on the topic[%d]: %s\n\r", tid, topic->subject); return; } void Channel::Lock(ACCOUNT *pAcnt, char *argument ) { if(argument[0] == '\0' ) { ptc(pAcnt, "{DSyntax{r: {W!lock list{x\n\r"); if(pAcnt->level > 1 ) ptc(pAcnt, " {r<{Wtopic id #{r>\n\r"); } if(!is_number(argument ) ) { if(!strcasecmp(argument, "list") ) pAcnt->showLocked(this); else { ptc(pAcnt, "{DSyntax{r: {W!lock list{x\n\r"); if(pAcnt->level > 1 ) ptc(pAcnt, " {r<{Wtopic id #{r>\n\r"); } return; } if(pAcnt->level <= 1 ) { ptc(pAcnt, "{DSyntax{r: {W!lock list{x\n\r"); return; } int tid = atoi(argument); Topic *topic; if( !(topic = getTopic(tid) ) ) { ptc(pAcnt, "You can only ignore topics that exist.\n\r"); return; } if(topic->locked) { topic->Unlock(); ptc(pAcnt, "Topic %d on %s has been locked!\n\r", tid, name); return; } topic->Lock(); ptc(pAcnt,"Topic %d on %s has been locked!\n\r", tid, name); return; } void Channel::Acknowledge(ACCOUNT *pAcnt, char* argument) { if(argument[0] == '\0') { ptc(pAcnt, "{DSyntax{r: {W!acknowledge {r<{Wtopic id #{r>{x\n\r"); return; } if(!is_number(argument ) ) { ptc(pAcnt, "You must use the topics ID to acknowledge it!\n\r"); return; } int tid = atoi(argument); Topic *topic; if( !(topic = getTopic(tid) ) ) { ptc(pAcnt, "You can't acknowledge topics that don't exist!\n\r"); return; } if( !pAcnt->isIgnoring(topic, bit) ) { ptc(pAcnt, "You have to be ignoring it to acknowledge it!\n\r"); return; } pAcnt->remIgnore(topic, bit); ptc(pAcnt, "You will now acknowlege all posts on topic[%d]: %s.\n\r", tid, topic->subject); return; } void Channel::On(ACCOUNT *pAcnt, char* argument) { if(IS_SET(pAcnt->channel, bit) ) { ptc(pAcnt, "This channel is already on.\n\r"); return; } ptc(pAcnt, "%s has been turned on.\n\r", name); SET_BIT(pAcnt->channel, bit); } void Channel::Off(ACCOUNT *pAcnt, char* argument) { if(!IS_SET(pAcnt->channel, bit) ) { ptc(pAcnt, "This channel is already off.\n\r"); return; } ptc(pAcnt, "%s has been turned off.\n\r", name); UNSET_BIT(pAcnt->channel, bit); } Channel::Channel( const Channel &rhs ) { } Topic::Topic(Channel *parent ): name(NULL), subject(NULL), id(++parent->lastTID), on(parent), lastPID(0), locked(false) { on->topics.push_back(this); } Topic::~Topic() { std::list<Post *>::iterator i; std::list<Topic *>::iterator n; Post *pst; free_string(name); free_string(subject); for( i = posts.begin() ; i != posts.end() ; ) { pst = *i; ++i; delete pst; } for( n = on->topics.begin() ; n != on->topics.end(); ++n ) if( *n == this ) { on->topics.erase(n); break; } } //Output: //[ChanName] Davion has started a new Topic[T:#]: Subject Line here. void Topic::Send( ) { std::string str; ACCOUNT *pAcnt; for(pAcnt = account_list ; pAcnt ; pAcnt = pAcnt->next) { if(!pAcnt->canSend(this, on) ) continue; str.clear(); str = pAcnt->genChanPrefix(NULL,this); on->Send(pAcnt, str); } Log(); } Topic::Topic(const Topic &rhs ) { } Post *Topic::getPost(int id) { std::list<Post *>::iterator i; if(id == -1 ) return NULL; for( i = posts.begin() ; i != posts.end() ; ++i ) if( (*i)->id == id ) return (*i); return NULL; } //Query /*INSERT INTO `Topics` ( `name` , `subject` , `id` , `chanId` , `lastpid` ) VALUES ( 'Davion', 'The subject', '1', '10', '1' );*/ void Topic::Log() { char query[MSL]; char safeSub[MSL*2]; mysql_real_escape_string(db, safeSub, subject, strlen(subject)); sprintf(query, "INSERT INTO `Topics` ( `name` , `subject` , `id` , `chanId` , `lastpid` ) VALUES ( '%s', '%s', '%d', '%d', '%d' );", name, safeSub, id, on->bit, lastPID); if(mysql_real_query(db, query, strlen(query) ) ) { logfp(LOG_BUG, "Topic::Log(): %s", mysql_error(db) ); return; } } void Topic::LoadPosts() { char query[MSL]; MYSQL_RES *res; MYSQL_ROW row; int maxPosts; sprintf(query, "SELECT * FROM Posts WHERE chanId=%d && topicid=%d", on->bit, id ); logfp(LOG_BUG, "Topic::LoadPosts: Begining to load. %s", query ); if( mysql_real_query(db, query, strlen(query) ) ) { logfp(LOG_BUG, "Topic::LoadPosts[C:%d][T:%d]: %s", id, on->bit, mysql_error(db) ); return; } if( !(res = mysql_store_result(db) ) ) { logfp(LOG_BUG, "Topic::LoadPosts[C:%d][T:%d]: Result null!", id, on->bit ); mysql_free_result(res); return; } if( ( maxPosts = mysql_num_rows(res) ) <= 0 ) { logfp(LOG_BUG, "Topic::LoadPosts[C:%d][T:%d]: No rows!", id, on->bit ); mysql_free_result(res); return; } for( int i = 0; i < maxPosts ; ++i ) { Post *post, *rpost; int replyPost; row = mysql_fetch_row(res); replyPost = atoi(row[2]); if( replyPost != -1 ) { if( !( rpost = getPost(replyPost) ) ) { logfp(LOG_BUG, "Topic::LoadPosts: rpost is null for %d.", replyPost); continue; } post = new Post(rpost); } else post = new Post(this); post->poster = str_dup(row[0]); post->msg = str_dup(row[1]); logfp(LOG_BUG, "\tLoading: %s[%d][%d]", post->poster, post->id, replyPost); } mysql_free_result(res); } void Topic::PostHist( ACCOUNT *pAcnt, char *argument ) { int page=10; std::list<Post *>::iterator i; if(argument[0] != '\0' && is_number(argument) ) page = atoi(argument); if(page <= 0 ) page = 1; if(page != 1) ptc(pAcnt, "These are the last %d posts made on %s under %s\n\r", page, on->name, subject); else ptc(pAcnt, "This is the last post made on %s under %s\n\r", on->name,subject); for(i = posts.begin() ; i != posts.end() && page > 0 ; ++i, --page) ptc(pAcnt,"{r{r[{W%s{r]{W%s %s\n\r", on->name, pAcnt->genChanPrefix((*i)), (*i)->msg); } Post::Post(Topic *parent ): poster(NULL), parentID(-1), id(++parent->lastPID), on(parent), msg(NULL), timeStamp(current_time) { on->posts.push_back(this); } Post::Post(Post *parent): poster(NULL), parentID(parent->id), id(++parent->on->lastPID), on(parent->on), msg(NULL), timeStamp(current_time) { parent->replies.push_back(this); on->posts.push_back(this); } Post::~Post() { std::list<Post *>::iterator i; Post *parent; free_string(poster); free_string(msg); for( i = on->posts.begin() ; i != on->posts.end() ; ++i ) if(this == *i) { on->posts.erase(i); break; } if(parentID != -1 ) { if( (parent = on->getPost(parentID) ) ) { for( i = parent->replies.begin() ; i != parent->replies.end() ; ++i ) if((*i) == this ) { parent->replies.erase(i); break; } } } } //Default Output //if replying //[ChanName][T:#][P:#] In reply to Davion[P:#] Descil says: This my reply to you! //else //[ChanName][T:#][P:#] Descil says: This is my post! void Post::Send() { std::string str; ACCOUNT *pAcnt; for(pAcnt = account_list ; pAcnt ; pAcnt = pAcnt->next) { if(!pAcnt->canSend(on, on->on) ) continue; str.clear(); str = pAcnt->genChanPrefix(this); str += " "; str += msg; on->on->Send(pAcnt, str); } Log(); return; } //Query /*INSERT INTO `Posts` ( `poster` , `msg` , `replyid` , `id` , `topicid` , `timestamp` ) VALUES ( 'Davion', 'This is the test message', '-1', '1', '1', NOW( ) ); */ void Post::Log() { char query[MSL*3]; char safeMsg[MSL*2]; mysql_real_escape_string(db, safeMsg,msg, strlen(msg)); sprintf(query,"INSERT INTO `Posts` ( `poster` , `msg` , `replyid` , `id` , `topicid`, `chanId`, `timestamp` ) VALUES ( '%s', '%s', '%d', '%d', '%d', '%d' , NOW() );", poster, safeMsg, parentID, id, on->id, on->on->bit); if(mysql_real_query(db, query, strlen(query)) ) { logfp(LOG_BUG, "Post::Log: %s\n\r\t%s", mysql_error(db), query ); return; } return; }