#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;
}