void askForUsers(plugin_pipe p, QString channel) { for(int i=0; i<5; i++) { p.write(who_is_here_message::create(channel)); this_thread::sleep_for(chrono::milliseconds(50)); } }
void run(plugin_pipe pipe, std::string internal_name) { // TODO remove this line once race condition is fixed pipe.write(register_handler::create(QObject::tr("/quip"), QuipPlugin::forward_quip_request)); // Construct a plugin object (used throughout) QuipPlugin plugin(internal_name, pipe); // Register for these message types with the following priorities: plugin.register_for_message_type(QObject::tr(LIRCH_MESSAGE_TYPE_HANDLER_READY)); plugin.register_for_message_type(QObject::tr(LIRCH_MESSAGE_TYPE_QUIP_REQUEST)); // Begin a loop that will only exit when sent the shutdown command while (plugin.has_not_received_shutdown_command()); }
void populateDefaultChannel(plugin_pipe p) { QSettings settings(QSettings::IniFormat, QSettings::UserScope, LIRCH_COMPANY_NAME, LIRCH_PRODUCT_NAME); settings.beginGroup("UserData"); QString defaultNick = settings.value("nick",LIRCH_DEFAULT_NICK).value<QString>(); settings.sync(); settings.endGroup(); askForUsers(p,"default"); p.write(nick_message::create(defaultNick, true)); }
void run(plugin_pipe p, string name) { if (name=="userlist") { p.write(registration_message::create(0, name, "userlist")); bool ready=false; while (!ready) { message m=p.blocking_read(); if (m.type!="registration_status") return; ready=dynamic_cast<registration_status *>(m.getdata())->status; } p.write(plugin_adder::create("real_userlist", "lib/libuserlist.so")); if (!test_userlist(p)) { p.write(core_quit_message::create()); return; } } p.write(core_quit_message::create()); cout << "Success!" << endl; }
bool test_userlist(plugin_pipe p) { message m=p.blocking_read(); if (m.type!="userlist") return false; auto u=dynamic_cast<userlist_message *>(m.getdata()); if (!u) return false; if (!u->statuses.empty()) return false; p.write(received_message::create(received_message_subtype::NORMAL, "POTUS", "James K Polk", "Invade Texas!", QHostAddress("3.4.18.45"))); this_thread::sleep_for(chrono::seconds(11)); m=p.read(); if (m.type!="userlist") return false; u=dynamic_cast<userlist_message *>(m.getdata()); if (!u) return false; if (u->statuses.size()!=1) return false; auto i=u->statuses.find("James K Polk"); if (i==u->statuses.end()) return false; if (i->second.nick!="James K Polk") return false; if (i->second.channels.size()!=1) return false; if (i->second.channels.find("POTUS")==i->second.channels.end()) return false; if (i->second.ip!=QHostAddress("3.4.18.45")) return false; auto diff=time(NULL)-i->second.lastseen; if (diff<10 || diff>12) return false; this_thread::sleep_for(chrono::minutes(2)); message mm=p.read(); while (mm.type!="") { m=mm; mm=p.read(); } if (m.type!="userlist") return false; u=dynamic_cast<userlist_message *>(m.getdata()); if (!u) return false; if (!u->statuses.empty()) return false; return true; }
void run(plugin_pipe p, std::string name) { // Register for the messages that pertain to the GUI p.write(registration_message::create(LIRCH_MSG_PRI_REG_MAX, name, LIRCH_MSG_TYPE_DISPLAY)); p.write(registration_message::create(LIRCH_MSG_PRI_REG_MAX, name, LIRCH_MSG_TYPE_USERLIST)); p.write(registration_message::create(LIRCH_MSG_PRI_REG_MAX, name, LIRCH_MSG_TYPE_CHANGED_NICK)); p.write(registration_message::create(LIRCH_MSG_PRI_REG_MAX, name, LIRCH_MSG_TYPE_SET_CHANNEL)); p.write(registration_message::create(LIRCH_MSG_PRI_REG_MAX, name, LIRCH_MSG_TYPE_LEAVE_CHANNEL)); extern LirchClientPipe mediator; mediator.open(p, QString::fromStdString(name)); // The mediator will act as a courier to the GUI while (mediator.ready()) { // Fetch a message from the pipe whenever it arrives message m = p.blocking_read(); // Determine what type of message it is if (m.type == LIRCH_MSG_TYPE_SHUTDOWN) { mediator.close(QObject::tr("core shutdown")); break; } else if (m.type == LIRCH_MSG_TYPE_REG_STAT) { // Recieved a registration status message auto reg = dynamic_cast<registration_status *>(m.getdata()); if (reg && !reg->status) { int registration_priority = reg->priority; // Retry over 9000 times until we succeed if (registration_priority < LIRCH_MSG_PRI_REG_MIN) { mediator.close(QObject::tr("failed to register with core")); break; } else { // Try again to register, if necessary p.write(registration_message::create(--registration_priority, name, reg->type)); } } } else if (m.type == LIRCH_MSG_TYPE_DISPLAY) { auto data = dynamic_cast<display_message *>(m.getdata()); if (data) { mediator.display(*data); } p.write(m.decrement_priority()); } else if (m.type == LIRCH_MSG_TYPE_USERLIST) { auto data = dynamic_cast<userlist_message *>(m.getdata()); if (data) { mediator.userlist(*data); } p.write(m.decrement_priority()); } else if (m.type == LIRCH_MSG_TYPE_CHANGED_NICK) { auto data = dynamic_cast<changed_nick_message *>(m.getdata()); if (data) { mediator.nick(*data); } p.write(m.decrement_priority()); } else if (m.type == LIRCH_MSG_TYPE_SET_CHANNEL) { auto data = dynamic_cast<set_channel_message *>(m.getdata()); if (data) { mediator.channel(*data); } p.write(m.decrement_priority()); } else if (m.type == LIRCH_MSG_TYPE_LEAVE_CHANNEL) { auto data = dynamic_cast<leave_channel_message *>(m.getdata()); if (data) { mediator.channel(*data); } p.write(m.decrement_priority()); } else { // By default, echo the message with decremented priority p.write(m.decrement_priority()); } } // We only get here through anomalous behavior (though it is good to have a catch-all) if (mediator.ready()) { mediator.close(); } }
void run(plugin_pipe p, string name) { p.write(registration_message::create(0, name, "userlist_request")); p.write(registration_message::create(0, name, "userlist_timer")); p.write(registration_message::create(30000, name, "received")); p.write(registration_message::create(30000, name, "received_me")); unordered_map<QString, user_status> statuses; while (true) { message m=p.blocking_read(); if (m.type=="shutdown") { return; } else if (m.type=="registration_status") { auto s=dynamic_cast<registration_status *>(m.getdata()); if (!s) continue; if (!s->status) { if ((0>=s->priority && s->priority>-200) || (30000>=s->priority && s->priority>29000)) p.write(registration_message::create(s->priority-1, name, s->type)); else return; } else { if (s->type=="userlist_timer") p.write(userlist_timer::create(10000)); } } else if (m.type=="userlist_request") { p.write(userlist_message::create(statuses)); } else if (m.type=="userlist_timer") { auto s=dynamic_cast<userlist_timer *>(m.getdata()); if (!s) continue; time_t now=time(NULL); //Remove all nicks that haven't been seen in 2 minutes decltype(statuses.begin()) i; while ((i=std::find_if(statuses.begin(), statuses.end(), [now](const std::pair<const QString &, const user_status &> &p) {return p.second.lastseen<now-2*60;}))!=statuses.end()) statuses.erase(i); p.write(userlist_message::create(statuses)); int msecs=s->msecs; thread th([&p,msecs]() { plugin_pipe pp; this_thread::sleep_for(chrono::milliseconds(msecs)); pp.write(userlist_timer::create(msecs)); }); th.detach(); } else if (m.type=="received") { auto s=dynamic_cast<received_message *>(m.getdata()); if (!s) continue; p.write(m.decrement_priority()); statuses[s->nick].nick=s->nick; statuses[s->nick].channels.insert(s->channel); statuses[s->nick].ip=s->ipAddress; statuses[s->nick].lastseen=time(NULL); } else if (m.type=="received_me") { auto s=dynamic_cast<received_me_message *>(m.getdata()); if (!s) continue; p.write(m.decrement_priority()); statuses[s->nick].nick=s->nick; statuses[s->nick].channels.insert(s->channel); statuses[s->nick].ip=s->ipAddress; statuses[s->nick].lastseen=time(NULL); } else p.write(m.decrement_priority()); } }
//updates all of the relevent fields of out status map based on the received message void updateSenderStatus(plugin_pipe p, message m, unordered_map<QString, user_status> & userList, QString currentNick) { if (m.type=="received") { auto msg=dynamic_cast<received_message *>(m.getdata()); if (!msg) { return; } userList[msg->nick].nick=msg->nick; userList[msg->nick].channels.insert(msg->channel); userList[msg->nick].ip=msg->ipAddress; userList[msg->nick].lastseen=time(NULL); } if (m.type=="received_status") { auto msg=dynamic_cast<received_status_message *>(m.getdata()); if (!msg) { return; } userList[msg->nick].lastseen=time(NULL); userList[msg->nick].nick=msg->nick; userList[msg->nick].ip=msg->ipAddress; if (msg->subtype==received_status_message_subtype::LEFT) { userList[msg->nick].channels.erase(msg->channel); if (msg->channel=="") { for(const auto & iterator:userList[msg->nick].channels) p.write(notify_message::create(iterator,msg->nick + " has logged off.")); userList.erase(msg->nick); } else p.write(notify_message::create(msg->channel,msg->nick+" has left channel "+msg->channel+".")); } else if (msg->subtype==received_status_message_subtype::JOIN) { //Only notify people if they're joining a channel they're not in if (userList[msg->nick].channels.count(msg->channel)) { return; } userList[msg->nick].channels.insert(msg->channel); p.write(notify_message::create(msg->channel,msg->nick+" has joined channel "+msg->channel+".")); } else if (msg->subtype==received_status_message_subtype::HERE && msg->channel!="") { userList[msg->nick].channels.insert(msg->channel); } else if (msg->subtype==received_status_message_subtype::WHOHERE) { // Only reply that you're in channels you're in if (userList[currentNick].channels.count(msg->channel)) { p.write(here_message::create(msg->channel)); userList[msg->nick].channels.insert(msg->channel); } } else if (msg->subtype==received_status_message_subtype::NICK) { //message->channel is storing the old nickname of the user in the case that it is a nick type received. //if it is, remove the person of the old nickname user_status oldNickInfo = userList[msg->channel]; oldNickInfo.nick=msg->nick; userList.erase(msg->channel); userList[msg->nick]=oldNickInfo; for(const auto & iterator:userList[msg->nick].channels) p.write(notify_message::create(iterator,msg->channel+" has changed their nick to "+msg->nick+".")); } } p.write(userlist_message::create(currentNick, userList)); }
void run(plugin_pipe p, string name) { p.write(registration_message::create(0, name, "userlist_request")); p.write(registration_message::create(0, name, "userlist_timer")); p.write(registration_message::create(30000, name, "received")); p.write(registration_message::create(30000, name, "received_status")); p.write(registration_message::create(0, name, "list_channels")); p.write(registration_message::create(0, name, "handler_ready")); p.write(registration_message::create(0, name, "leave_channel")); p.write(registration_message::create(0, name, "set_channel")); p.write(registration_message::create(-30000, name, "nick")); p.write(registration_message::create(-30000, name, "who_is")); p.write(registration_message::create(-30000, name, "block name")); bool firstTime=true; unordered_map<QString, user_status> userList; QString currentNick = LIRCH_DEFAULT_NICK; std::thread populate(populateDefaultChannel,p); populate.detach(); while (true) { message m=p.blocking_read(); if (m.type=="shutdown") { return; } else if (m.type=="registration_status") { auto s=dynamic_cast<registration_status *>(m.getdata()); if (!s) continue; if (!s->status) { if ((0>=s->priority && s->priority>-200) || (30000>=s->priority && s->priority>29000)) p.write(registration_message::create(s->priority-1, name, s->type)); else return; } else { if (s->type=="userlist_timer") { p.write(userlist_timer::create()); } else if (s->type=="handler_ready") { p.write(register_handler::create("/list", sendList)); p.write(register_handler::create("/nick", sendNick)); p.write(register_handler::create("/whois", sendWhois)); } } } else if (m.type=="userlist_request") { p.write(userlist_message::create(currentNick, userList)); } else if (m.type=="userlist_timer") { time_t now=time(NULL); //Remove all nicks that haven't been seen in 2 minutes decltype(userList.begin()) i; while ((i=std::find_if(userList.begin(), userList.end(), [now](const std::pair<const QString &, const user_status &> &p) {return p.second.lastseen<now-2*60;}))!=userList.end()) { for(auto iter = i->second.channels.begin(); iter!=i->second.channels.end(); iter++) p.write(notify_message::create(*iter, i->first + " has logged off.")); userList.erase(i); } p.write(userlist_message::create(currentNick, userList)); thread([](plugin_pipe p) { this_thread::sleep_for(chrono::seconds(10)); p.write(userlist_timer::create()); }, p).detach(); } else if (m.type=="handler_ready") { p.write(register_handler::create("/list", sendList)); p.write(register_handler::create("/nick", sendNick)); p.write(register_handler::create("/whois", sendWhois)); } else if (m.type=="received" || m.type=="received_status") { p.write(m.decrement_priority()); updateSenderStatus(p,m,userList,currentNick); } else if (m.type=="nick") { auto s=dynamic_cast<nick_message *>(m.getdata()); if (!s) continue; p.write(m.decrement_priority()); if (firstTime) p.write(set_channel_message::create("default")); p.write(setNick(userList,currentNick,*s,firstTime)); //The userlist is no longer a virgin firstTime = false; if (s->changeDefault) { QSettings settings(QSettings::IniFormat, QSettings::UserScope, LIRCH_COMPANY_NAME, LIRCH_PRODUCT_NAME); settings.beginGroup("UserData"); settings.setValue("nick", currentNick); settings.sync(); settings.endGroup(); } } else if (m.type=="list_channels") { auto s=dynamic_cast<list_channels *>(m.getdata()); if (!s) continue; for (auto &i : userList) { if (s->filterChannel=="" || i.second.channels.count(s->filterChannel)!=0) { QStringList channelList; for (auto &c : i.second.channels) channelList.append(c); p.write(notify_message::create(s->destinationChannel, QObject::tr("User %1 (%2) was last seen at %3 and is in the following channels: %4").arg(i.second.nick, i.second.ip.toString(), QDateTime::fromTime_t(i.second.lastseen).toString(), channelList.join(" ")))); } } } else if (m.type == "set_channel") { auto s=dynamic_cast<set_channel_message *>(m.getdata()); if (!s) continue; p.write(m.decrement_priority()); // If you can't in the channel yet, ask who's there if (userList[currentNick].channels.count(s->channel)==0) askForUsers(p,s->channel); } else if (m.type == "leave_channel") { auto s=dynamic_cast<leave_channel_message *>(m.getdata()); if (!s) continue; p.write(m.decrement_priority()); for(auto & person:userList) { person.second.channels.erase(s->channel); } } else if (m.type == "who_is") { auto s=dynamic_cast<who_is_message *>(m.getdata()); if (!s) continue; p.write(m.decrement_priority()); if (userList.count(s->nick)==0) continue; QStringList channelList; for (auto &c : userList[s->nick].channels) channelList.append(c); p.write(notify_message::create(s->channel, QObject::tr("User %1 (%2) was last seen at %3 and is in the following channels: %4").arg(userList[s->nick].nick, userList[s->nick].ip.toString(), QDateTime::fromTime_t(userList[s->nick].lastseen).toString(), channelList.join(" ")))); } else p.write(m.decrement_priority()); } }
void runplugin(plugin_pipe &p, const string &name) { text_line input; QString channel="default"; int maxx, maxy; getmaxyx(stdscr, maxy, maxx); //10000 lines of scrollback should be enough for anyone //WindowWrapper channel_output=newpad(10000, maxx); unordered_map<QString, WindowWrapper> channel_windows; if (channel_windows["default"]==nullptr) return; WindowWrapper input_display=newwin(1, maxx-1, maxy-1, 0); if (input_display==nullptr) return; //Be lazy and let the input scroll scrollok(input_display, TRUE); //p.write(registration_message::create(-30000, name, "display")); while (true) { wint_t key; int rc=get_wch(&key); if (rc==OK) { if (key=='\r' || key=='\n') { p.write(raw_edict_message::create(input.getQString(),channel)); input.kill_whole_line(); } else if (key==(unsigned char)CTRL('U')) { input.backward_kill_line(); } else if (key==(unsigned char)CTRL('H')) { input.backward_delete_char(); } else if (key==WEOF) { //Quit since we have no more input break; } else { //This conversion is always valid, since key's //only valid values are valid wchar_t values //and WEOF, and we know it's not WEOF. input.insert(key); } } else if (rc==KEY_CODE_YES) { if (key==KEY_BACKSPACE) { input.backward_delete_char(); } else if (key==KEY_ENTER) { p.write(raw_edict_message::create(input.getQString(),channel)); input.kill_whole_line(); } } while (p.has_message()) { message m=p.read(); if (m.type=="shutdown") { return; } else if (m.type=="registration_status") { auto s=dynamic_cast<registration_status *>(m.getdata()); if (!s) continue; if (!s->status) { if (s->priority>-32000) p.write(registration_message::create(s->priority-1, name, s->type)); } } else if (m.type=="display") { auto s=dynamic_cast<display_message *>(m.getdata()); if (!s) continue; p.write(m.decrement_priority()); QString message_channel=s->channel; string nick=s->nick.toLocal8Bit().constData(); string contents=s->contents.toLocal8Bit().constData(); if (s->channel=="") message_channel=channel; if (channel_windows.count(message_channel)!=0) { if(s->subtype==display_message_subtype::NORMAL) wprintu(channel_windows[message_channel], "<%s> %s\n", nick.c_str(), contents.c_str()); if(s->subtype==display_message_subtype::ME) wprintu(channel_windows[message_channel], "* %s %s\n", nick.c_str(), contents.c_str()); if(s->subtype==display_message_subtype::NOTIFY) wprintu(channel_windows[message_channel], QString((QChar[]){0x203C, 0x203D, ' ', '%', 's', '\n', 0}).toLocal8Bit().constData(), contents.c_str()); if(s->subtype==display_message_subtype::NOTIFY_CURRENT) wprintu(channel_windows[message_channel], QString((QChar[]){0x203C, 0x203C, 0x203D, ' ', '%', 's', '\n', 0}).toLocal8Bit().constData(), contents.c_str()); } } else if (m.type=="set_channel") { auto i=dynamic_cast<set_channel_message *>(m.getdata()); if (!i) continue; p.write(m.decrement_priority()); if (i->channel=="") p.write(notify_message::create(channel, "On channel "+channel)); else channel=i->channel; } else if (m.type=="leave_channel") { auto i=dynamic_cast<leave_channel_message *>(m.getdata()); if (!i) continue; p.write(m.decrement_priority()); if (i->channel=="") i->channel=channel; channel_windows.erase(i->channel); if (i->channel==channel) p.write(set_channel_message::create(channel)); } else p.write(m.decrement_priority()); }