/* * web.cpp * web-based functions * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * Permission to use, modify and distribute is granted via the * Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell * Contributions by Tim Callahan, Jonathan Hseu * Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman * */ #include "mud.h" #include "web.h" #include "clans.h" #include "commands.h" #include "guilds.h" // C includes #include <errno.h> #include <string.h> #include <sys/stat.h> int lastmod = 0; struct stat lm_check; // Static initialization const char* WebInterface::fifoIn = "webInterface.in"; const char* WebInterface::fifoOut = "webInterface.out"; WebInterface* WebInterface::myInstance = NULL; static char ETX = 3; static char EOT = 4; //static char ETB = 23; static char itemDelim = 6; static char innerDelim = 7; static char startSubDelim = 8; static char endSubDelim = 9; static char equipDelim = 10; //********************************************************************* // updateRecentActivity //********************************************************************* void updateRecentActivity() { callWebserver((bstring)"mud.php?type=recent", true, true); } //********************************************************************* // latestPost //********************************************************************* void latestPost(const bstring& view, const bstring& subject, const bstring& username, const bstring& boardname, const bstring& post) { if(view == "" || boardname == "" || username == "" || post == "") return; for(Socket* sock : gServer->sockets) { const Player* player = sock->getPlayer(); if(!player || player->fd < 0) continue; if(player->flagIsSet(P_HIDE_FORUM_POSTS)) continue; if(view == "dungeonmaster" && !player->isDm()) continue; if(view == "caretaker" && !player->isCt()) continue; if(view == "watcher" && !player->isWatcher()) continue; if(view == "builder" && !player->isStaff()) continue; bool fullMode = player->flagIsSet(P_FULL_FORUM_POSTS); if(player->inCombat()) fullMode = false; if(fullMode) player->printColor("%s", post.c_str()); else player->printColor("^C==>^x There is a new post titled ^W\"%s\"^x by ^W%s^x in the ^W%s^x board.\n", subject.c_str(), username.c_str(), boardname.c_str()); } } //********************************************************************* // WebInterface //********************************************************************* // Verify the in/out fifos exist and open the in fifo in non-blocking mode WebInterface::WebInterface() { openFifos(); } WebInterface::~WebInterface() { closeFifos(); } //********************************************************************* // open //********************************************************************* void WebInterface::openFifos() { checkFifo(fifoIn); checkFifo(fifoOut); char filename[80]; snprintf(filename, 80, "%s/%s", Path::Game, fifoIn); inFd = open(filename, O_RDONLY|O_NONBLOCK); if(inFd == -1) throw(std::runtime_error("WebInterface: Unable to open " + bstring(filename) + ":" +strerror(errno))); outFd = -1; std::cout << "WebInterface: monitoring " << fifoIn << std::endl; } //********************************************************************* // close //********************************************************************* void WebInterface::closeFifos() { // unlink(gifo); close(outFd); close(inFd); } //********************************************************************* // initWebInterface //********************************************************************* bool Server::initWebInterface() { // Do a check for main port, hostname, etc here webInterface = WebInterface::getInstance(); return(true); } //********************************************************************* // checkWebInterface //********************************************************************* bool Server::checkWebInterface() { if(!webInterface) return(false); return(webInterface->handleRequests()); } //********************************************************************* // recreateFifos //********************************************************************* void Server::recreateFifos() { if(webInterface) webInterface->recreateFifos(); } void WebInterface::recreateFifos() { char filename[80]; closeFifos(); snprintf(filename, 80, "%s/%s.xml", Path::Game, fifoIn); unlink(filename); snprintf(filename, 80, "%s/%s.xml", Path::Game, fifoOut); unlink(filename); openFifos(); } //********************************************************************* // checkFifo //********************************************************************* // Check that the given fifo exists as a fifo. If it is a regular file, erase it. // If it doesn't exist, create it. bool WebInterface::checkFifo(const char* fifoFile) { struct stat statInfo; int retVal; bool needToCreate = false; char filename[80]; snprintf(filename, 80, "%s/%s", Path::Game, fifoFile); retVal = stat(filename, &statInfo); if(retVal == 0) { if((statInfo.st_mode & S_IFMT) != S_IFIFO) { if(unlink(filename) != 0) throw bstring("WebInterface: Unable to unlink " + bstring(fifoFile) + ":" + strerror(errno)); needToCreate = true; } } else { needToCreate = true; } if(needToCreate) { retVal = mkfifo(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); if(retVal != 0) throw bstring("WebInterface: Unable to mkfifo " + bstring(fifoFile) + ":" + strerror(errno)); } return(true); } //********************************************************************* // getInstance //********************************************************************* WebInterface* WebInterface::getInstance() { if(myInstance == NULL) myInstance = new WebInterface; return(myInstance); } //********************************************************************* // destroyInstance //********************************************************************* void WebInterface::destroyInstance() { if(myInstance != NULL) delete myInstance; myInstance = NULL; } //********************************************************************* // handleRequests //********************************************************************* bool WebInterface::handleRequests() { checkInput(); handleInput(); sendOutput(); return(true); } //********************************************************************* // checkInput //********************************************************************* bool WebInterface::checkInput() { if(inFd == -1) return(false); char tmpBuf[1024]; int n = 0, total = 0; do { // Attempt to read from the socket n = read(inFd, tmpBuf, 1023); if(n <= 0) { //std::cout << "WebInterface: Unable to read - " << strerror(errno) << "\n"; break; } tmpBuf[n] = '\0'; total += n; inBuf += tmpBuf; } while(n > 0); inBuf.Replace("\r", "\n"); if(total == 0) return(false); return(true); } //********************************************************************* // handleInput //********************************************************************* bool WebInterface::messagePlayer(bstring command, bstring tempBuf) { // MSG / SYSMSG = system sending // TXT = user sending bstring::size_type pos=0; // we don't know the command yet if(command == "") { pos = tempBuf.Find(' '); if(pos == bstring::npos) command = tempBuf; else command = tempBuf.left(pos); } pos = tempBuf.Find(' '); if(pos == bstring::npos) { std::cout << "WebInterface: Messaging failed; not enough data!" << std::endl; return(""); } bstring user = tempBuf.left(pos); user = user.toLower(); user.setAt(0, up(user.getAt(0))); tempBuf.erase(0, pos+1); // Clear out the user tempBuf = tempBuf.trim(); std::cout << "WebInterface: Messaging user " << user << std::endl; const Player* player = gServer->findPlayer(user); if(!player) { outBuf += "That player is not logged on."; } else { bool txt = (command == "TXT"); if(txt && ( player->getClass() == BUILDER || player->flagIsSet(P_IGNORE_ALL) || player->flagIsSet(P_IGNORE_SMS) )) { std::cout << "WebInterface: Messaging failed; user does not want text messages." << std::endl; return(false); } if(command != "SYSMSG") player->printColor("^C==> "); if(!txt) { tempBuf.Replace("<cr>", "\n\t"); } player->printColor("%s%s%s\n", txt ? "^WText Msg: \"" : "", tempBuf.c_str(), txt ? "\"" : ""); if(!player->isDm()) { broadcast(watchingEaves, "^E--- %s to %s, \"%s\".", txt ? "Text message" : "System message", player->getCName(), tempBuf.c_str()); } } outBuf += EOT; return(true); } //********************************************************************* // getInventory //********************************************************************* bstring doGetInventory(const Player* player, const ObjectSet &set); bstring doGetInventory(const Player* player, Object* object, int loc=-1) { std::ostringstream oStr; oStr << itemDelim << object->getName() << innerDelim << object->getId() << innerDelim << object->getWearflag() << innerDelim << object->getType(); if(loc != -1) oStr << innerDelim << loc; if(!object->objects.empty()) { oStr << startSubDelim << doGetInventory(player, object->objects) << endSubDelim; } return(oStr.str()); } bstring doGetInventory(const Player* player, const ObjectSet &set) { bstring inv = ""; for(Object* obj : set ) { if(player->canSee(obj)) inv += doGetInventory(player, obj); } return(inv); } bstring getInventory(const Player* player) { std::ostringstream oStr; oStr << player->getUniqueObjId() << doGetInventory(player, player->objects) << equipDelim; for(int i=0; i<MAXWEAR; i++) { if(player->ready[i]) oStr << doGetInventory(player, player->ready[i], i); } return(oStr.str()); } //********************************************************************* // handleInput //********************************************************************* bool webWield(Player* player); bool webWear(Player* player); bool webUse(Player* player, int id, int type) { // Disabled return(false); // // switch(type) { // case WEAPON: // return(webWield(player, id)); // case ARMOR: // return(webWear(player, id)); // case POTION: // //return(cmdConsume(player, id)); // case SCROLL: // //return(cmdReadScroll(player, id)); // case WAND: // //return(cmdUseWand(player, id)); // case KEY: // //return(cmdUnlock(player, id)); // case LIGHTSOURCE: // //return(cmdHold(player, id)); // default: // player->print("How does one use that?\n"); // return(false); // } } //********************************************************************* // handleInput //********************************************************************* // Uses ETX (End of Text) as abort and EOT (End of Transmission Block) as EOF bool WebInterface::handleInput() { if(inBuf.empty()) return(false); bstring::size_type start=0, idx=0, pos=0; bool needData=false; // First, see if we have a clear command (ETX), if so erase up to there idx = inBuf.ReverseFind(ETX); if(idx != bstring::npos) inBuf.erase(0, idx); // Now lets ignore any unprintable characters that might have gotten tacked on to the start while(!isPrintable((unsigned char)inBuf[start])) start++; inBuf.erase(0, start); // Now lets look for a command idx = inBuf.find("\n", 0); if(idx == bstring::npos) return(false); // Copy the entire command to a temporary buffer and lets see if we can find // a valid command bstring tempBuf = inBuf.substr(0, idx); // Don't copy the \n bstring command; // the command being run bstring dataBuf; // the buffer for incoming data pos = tempBuf.Find(' '); if(pos == bstring::npos) command = tempBuf; else command = tempBuf.left(pos); idx += 1; // Consume the \n if(inBuf[idx] == '\n') idx += 1; // Consume the extra \n if applicable //const unsigned char* before = (unsigned char*)strdup(inBuf.c_str()); // certain commands can only be called from the webserver if(gConfig->getWebserver() == "") { if( command == "FORUM" || command == "UNFORUM" || command == "AUTOGUILD" ) command = ""; } // If we can jump right in (LOAD), do so. If we need to wait until we've // read in the ETF from the builder, set needData to true if(command == "SAVE" || command == "LATESTPOST") needData = true; if( command == "LOAD" || command == "WHO" || command == "MSG" || command == "SYSMSG" || command == "TXT" || command == "WIKI" || command == "FINGER" || // data requirement is minimal for the following command == "WHOIS" || command == "FORUM" || command == "UNFORUM" || command == "AUTOGUILD" || command == "GETINVENTORY" || command == "EQUIP" || command == "UNEQUIP" || (needData && inBuf.find(EOT, idx) != bstring::npos) ) { // We've got a valid command so we can erase it out of the input buffer inBuf.erase(0, idx); //const unsigned char* leftOver = (unsigned char*)strdup(inBuf.c_str()); // If we need more data, we have the command terminated by a '\n' // and then the rest should be the data we need (xml we're supposed to save), terminated // by EOT, so copy that into buffer if(needData) { idx = inBuf.find(EOT, 0); // If we've gotten this far, we know the EOT exists, so no need to check the find result dataBuf = inBuf.substr(0, idx); // Don't copy the EOT inBuf.erase(0, idx+1); // but do erase the EOT } tempBuf.erase(0, pos+1); // Clear out the command // if(command == "GETINVENTORY" || command == "EQUIP" || command == "UNEQUIP") { // pos = tempBuf.Find(' '); // int id=0, type=0; // // bstring user = ""; // if(pos != bstring::npos) { // user = tempBuf.left(pos); // tempBuf.erase(0, pos+1); // Clear out the user // id = atoi(tempBuf.c_str()); // // if(command == "EQUIP") { // pos = tempBuf.Find(' '); // tempBuf.erase(0, pos+1); // Clear out the user // type = atoi(tempBuf.c_str()); // } // } else { // user = tempBuf; // tempBuf = ""; // } // // Player* player = 0; // if(user != "") // player = gServer->findPlayer(user); // if(!player) { // outBuf += EOT; // return(true); // } // // if(command == "GETINVENTORY") { // // outBuf += getInventory(player); // // } else if(command == "EQUIP") { // // outBuf += webUse(player, id, type) ? "1" : "0"; // // } else if(command == "UNEQUIP") { // // outBuf += webRemove(player, id) ? "1" : "0"; // // } // // outBuf += EOT; // return(true); // } else if(command == "WHO") { std::cout << "WebInterface: Checking for users online" << std::endl; outBuf += webwho(); outBuf += EOT; return(true); } else if(command == "WHOIS") { std::cout << "WebInterface: Whois for user " << tempBuf << std::endl; tempBuf = tempBuf.toLower(); tempBuf.setAt(0, up(tempBuf.getAt(0))); const Player* player = gServer->findPlayer(tempBuf); if( !player || player->flagIsSet(P_DM_INVIS) || player->isEffected("incognito") || player->isEffected("mist") || player->isInvisible() ) { outBuf += "That player is not logged on."; } else { outBuf += player->getWhoString(false, false, false); } outBuf += EOT; return(true); } else if(command == "FINGER") { std::cout << "WebInterface: Fingering user " << tempBuf << std::endl; outBuf += doFinger(0, tempBuf, 0); outBuf += EOT; return(true); } else if(command == "WIKI") { return(wiki(command, tempBuf)); } else if(command == "MSG" || command == "SYSMSG" || command == "TXT") { return(messagePlayer(command, tempBuf)); } else if(command == "FORUM") { pos = tempBuf.Find(' '); if(pos == bstring::npos) { std::cout << "WebInterface: Forum association failed; not enough data!" << std::endl; return(false); } bstring user = tempBuf.left(pos); tempBuf.erase(0, pos+1); // Clear out the user std::cout << "WebInterface: Forum association for user " << user << std::endl; Player* player = gServer->findPlayer(user); if(player) { player->setForum(tempBuf); player->save(true); return(messagePlayer("MSG", user + " Your character has been associated with forum account " + tempBuf + ".")); } // they logged off? if(!loadPlayer(user.c_str(), &player)) { // they were deleted? undo! webUnassociate(user); return(false); } player->setForum(tempBuf); player->save(); free_crt(player); outBuf += EOT; return(true); } else if(command == "UNFORUM") { std::cout << "WebInterface: Forum unassociation for user " << tempBuf << std::endl; if(tempBuf == "") { std::cout << "WebInterface: Forum unassociation failed; not enough data!" << std::endl; return(false); } tempBuf = tempBuf.toLower(); tempBuf.setAt(0, up(tempBuf.getAt(0))); Player* player = gServer->findPlayer(tempBuf); if(player) { if(player->getForum() != "") { messagePlayer("MSG", tempBuf + " Your character has been unassociated from forum account " + player->getForum() + "."); player->setForum(""); player->save(true); } } else { // they logged off? if(!loadPlayer(tempBuf.c_str(), &player)) { // they were deleted? no problem! } else { player->setForum(""); player->save(); free_crt(player); } } outBuf += EOT; return(true); } else if(command == "AUTOGUILD") { if(tempBuf == "") { std::cout << "WebInterface: Autoguild failed; not enough data!" << std::endl; return(false); } const Guild* guild=0; if(tempBuf.getLength() <= 40) guild = gConfig->getGuild(tempBuf); if(!guild) { std::cout << "WebInterface: Autoguild failed; guild " << tempBuf << " not found!" << std::endl; return(false); } std::list<bstring>::const_iterator it; Player* player=0; bool online=true; for(it = guild->members.begin() ; it != guild->members.end() ; it++ ) { online = true; player = gServer->findPlayer(*it); if(!player) { if(!loadPlayer((*it).c_str(), &player)) continue; online = false; } if(player->getForum() != "") callWebserver((bstring)"mud.php?type=autoguild&guild=" + guild->getName() + "&user=" + player->getForum() + "&char=" + player->getName()); if(!online) free_crt(player); } outBuf += EOT; return(true); } bstring type; CatRef cr; // We'll need these to load or save xmlNodePtr rootNode; xmlDocPtr xmlDoc; if(command == "LATESTPOST") { const unsigned char* latestBuffer = (unsigned char*)dataBuf.c_str(); if((xmlDoc = xmlParseDoc(latestBuffer)) == NULL) { std::cout << "WebInterface: LatestPost - Error parsing xml\n"; return(false); } rootNode = xmlDocGetRootElement(xmlDoc); xmlNodePtr curNode = rootNode->children; bstring view = "", subject = "", username = "", boardname = "", post = ""; while(curNode) { if(NODE_NAME(curNode, "View")) xml::copyToBString(view, curNode); else if(NODE_NAME(curNode, "Subject")) xml::copyToBString(subject, curNode); else if(NODE_NAME(curNode, "Username")) xml::copyToBString(username, curNode); else if(NODE_NAME(curNode, "Boardname")) xml::copyToBString(boardname, curNode); else if(NODE_NAME(curNode, "Post")) xml::copyToBString(post, curNode); curNode = curNode->next; } latestPost(view, subject, username, boardname, post); outBuf += EOT; return(true); } // The next 3 characters should be CRT, OBJ, or ROM and indicate what we're acting on type = tempBuf.left(3); tempBuf.erase(0, 4); // Now we need to find what area/index we're working on. If no area is found we assume misc getCatRef(tempBuf, &cr, 0); std::cout << "WebInterface: Found command: " << command << " " << type << " " << cr.area << "." << cr.id << std::endl; if(command == "LOAD") { // Loading: We should grab the most current copy of what they want, and write it // to the webInterface.out file xmlDoc = xmlNewDoc(BAD_CAST "1.0"); if(type == "CRT") { rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Creature", NULL); xmlDocSetRootElement(xmlDoc, rootNode); Monster *monster; if(loadMonster(cr, &monster)) { monster->saveToXml(rootNode, ALLITEMS, LS_FULL); std::cout << "Generated xml for " << monster->getName() << "\n"; free_crt(monster); } } else if(type == "OBJ") { rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Object", NULL); xmlDocSetRootElement(xmlDoc, rootNode); Object* object; if(loadObject(cr, &object)) { object->saveToXml(rootNode, ALLITEMS, LS_FULL); std::cout << "Generated xml for " << object->getName() << "\n"; delete object; } } else if(type == "ROM") { rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Room", NULL); xmlDocSetRootElement(xmlDoc, rootNode); UniqueRoom* room; if(loadRoom(cr, &room)) { room->saveToXml(rootNode, ALLITEMS); std::cout << "Generated xml for " << room->getName() << "\n"; } } // Save the xml document to a character array int len=0; unsigned char* tmp = NULL; xmlDocDumpFormatMemory(xmlDoc, &tmp,&len,1); xmlFreeDoc(xmlDoc); // Send the xml document to the output buffer and append an EOT so they // know where to stop reading this construct in outBuf += tmp; outBuf += EOT; // Don't forget to free the char array we got from xmlDocDumpFormatMemory free(tmp); } else if(command == "SAVE") { // Save - They are sending us an object/room/monster to save to the database and // update the queue with const unsigned char* saveBuffer = (unsigned char*)dataBuf.c_str(); if((xmlDoc = xmlParseDoc(saveBuffer)) == NULL) { std::cout << "WebInterface: Save - Error parsing xml\n"; return(false); } rootNode = xmlDocGetRootElement(xmlDoc); int num = xml::getIntProp(rootNode, "Num"); bstring newArea = xml::getProp(rootNode, "Area"); // Make sure they're sending us the proper index! if(num != cr.id || newArea != cr.area) { std::cout << "WebInterface: MisMatched save - Got " << num << " - " << newArea << " Expected " << cr.str() << "\n"; return(false); } if(type == "CRT") { Monster* monster = new Monster(); monster->readFromXml(rootNode); monster->saveToFile(); broadcast(isDm, "^y*** Monster %s - %s^y updated by %s.", monster->info.str().c_str(), monster->getCName(), monster->last_mod); gConfig->replaceMonsterInQueue(monster->info, monster); } else if(type == "OBJ") { Object* object = new Object(); object->readFromXml(rootNode); object->saveToFile(); broadcast(isDm, "^y*** Object %s - %s^y updated by %s.", object->info.str().c_str(), object->getCName(), object->lastMod.c_str()); gConfig->replaceObjectInQueue(object->info, object); } else if(type == "ROM") { UniqueRoom* room = new UniqueRoom(); room->readFromXml(rootNode); room->saveToFile(0); gConfig->reloadRoom(room); broadcast(isDm, "^y*** Room %s - %s^y updated by %s.", room->info.str().c_str(), room->getCName(), room->last_mod); } std::cout << "WebInterface: Saved " << type << " " << cr.str() << "\n"; } } return(true); } //********************************************************************* // sendOutput //********************************************************************* bool WebInterface::sendOutput() { if(outBuf.empty()) return(false); int written=0; int n=0; char filename[80]; snprintf(filename, 80, "%s/%s", Path::Game, fifoOut); int total = outBuf.length(); if(outFd == -1) outFd = open(filename, O_WRONLY|O_NONBLOCK); if(outFd == -1) { // std::cout << "WebInterface: Unable to open " << fifoOut << ":" << strerror(errno); return(false); } // Write directly to the socket, otherwise compress it and send it do { n = write(outFd, outBuf.c_str(), outBuf.length()); if(n < 0) { //std::cout << "WebInterface: sendOutput " << strerror(errno) << std::endl; return(false); } outBuf.erase(0, n); written += n; } while(written < total); return(true); } //********************************************************************* // webwho //********************************************************************* bstring webwho() { std::ostringstream oStr; const Player *player=0; for(std::pair<bstring, Player*> p : gServer->players) { player = p.second; if(!player->isConnected()) continue; if(player->getClass() == BUILDER) continue; if(player->flagIsSet(P_DM_INVIS)) continue; if(player->isEffected("incognito")) continue; if(player->isInvisible()) continue; if(player->isEffected("mist")) continue; bstring cls = getShortClassName(player); oStr << player->getLevel() << "|" << cls.left(4) << "|"; if(player->flagIsSet(P_OUTLAW)) oStr << "O"; else if((player->flagIsSet(P_NO_PKILL) || player->flagIsSet(P_DIED_IN_DUEL) || (player->getConstRoomParent()->isPkSafe())) && (player->flagIsSet(P_CHAOTIC) || player->getClan()) ) oStr << "N"; else if(player->flagIsSet(P_CHAOTIC)) // Chaotic oStr << "C"; else oStr << "L"; oStr << "|"; if(player->isPublicWatcher()) oStr << "(w)"; oStr << player->fullName() << "|" << gConfig->getRace(player->getDisplayRace())->getAdjective() << "|" << player->getTitle() << "|"; if(player->getClan()) oStr << "(" << gConfig->getClan(player->getClan())->getName() << ")"; else if(player->getDeity()) oStr << "(" << gConfig->getDeity(player->getDeity())->getName() << ")"; oStr << "|"; if(player->getGuild() && player->getGuildRank() >= GUILD_PEON) oStr << "[" << getGuildName(player->getGuild()) << "]"; oStr << "\n"; } long t = time(0); oStr << ctime(&t); return(oStr.str()); } //********************************************************************* // callWebserver //********************************************************************* // load the webserver with no return value // wget must be installed for this call to work // questionMark: is a question mark has already been added to the url (for GET parameters) void callWebserver(bstring url, bool questionMark, bool silent) { if(gConfig->getWebserver() == "" || url == "" || url.getLength() > 450) return; if(!silent) broadcast(isDm, "^yWebserver called: ^x%s", url.c_str()); // authorization query string if(gConfig->getQS() != "") { if(!questionMark) url += "?"; else url += "&"; url += gConfig->getQS(); } // build command here incase we ever want to audit char command[512]; sprintf(command, "wget \"%s%s\" -q -O /dev/null", gConfig->getWebserver().c_str(), url.c_str()); // set the user agent, if applicable if(gConfig->getUserAgent() != "") { strcat(command, " -U \""); strcat(command, gConfig->getUserAgent().c_str()); strcat(command, "\""); } if(!fork()) { system(command); exit(0); } } //********************************************************************* // dmFifo //********************************************************************* // delete and recreate the fifos int dmFifo(Player* player, cmd* cmnd) { gServer->recreateFifos(); player->print("Interface fifos have been recreated.\n"); return(0); } //********************************************************************* // cmdForum //********************************************************************* int cmdForum(Player* player, cmd* cmnd) { if(player->getProxyName() != "") { *player << "You are unable to modify forum accounts of proxied characters.\n"; return(0); } bstring::size_type pos=0; std::ostringstream url; url << "mud.php?type=forum&char=" << player->getName(); bstring user = cmnd->str[1]; bstring pass = getFullstrText(cmnd->fullstr, 2, ' '); pos = user.Find('&'); if(pos != bstring::npos) user = ""; if(user == "remove" && player->getForum() != "") { player->printColor("Attempting to unassociate this character with forum account: ^C%s\n", player->getForum().c_str()); player->setForum(""); player->save(true); webUnassociate(player->getName()); return(0); } else if(user != "" && pass != "") { pass = md5(pass); player->printColor("Attempting to associate this character with forum account: ^C%s\n", user.c_str()); url << "&user=" << user << "&pass=" << pass; callWebserver(url.str()); // don't leave their password in last command player->setLastCommand(cmnd->str[0] + (bstring)" " + cmnd->str[1] + (bstring)" ********"); return(0); } else if(player->getForum() == "") { player->print("There is currently no forum account associated with this character.\n"); } else { player->printColor("Forum account associated with this character: ^C%s\n", player->getForum().c_str()); player->print("To unassociate this character from this forum account type:\n"); player->printColor(" forum ^Cremove\n"); } player->print("To associate this character with a forum account type:\n"); player->printColor(" forum ^C<forum account> <forum password>\n\n"); player->printColor("Type ^WHELP LATEST_POST^x to see the latest forum post.\n"); player->printColor("Type ^WHELP LATEST_POSTS^x to see the 6 most recent forum posts.\n"); return(0); } //********************************************************************* // webUnassociate //********************************************************************* void webUnassociate(bstring user) { callWebserver("mud.php?type=forum&char=" + user + "&delete"); } //********************************************************************* // webCrash //********************************************************************* void webCrash(bstring msg) { callWebserver("mud.php?type=crash&msg=" + msg); } //********************************************************************* // cmdWiki //********************************************************************* // This function allows a player to loop up a wiki entry int cmdWiki(Player* player, cmd* cmnd) { struct stat f_stat; char file[80]; std::ostringstream url; bstring entry = getFullstrText(cmnd->fullstr, 1); player->clearFlag(P_AFK); if(player->isBraindead()) { player->print("You are brain-dead. You can't do that.\n"); return(0); } if(entry == "") { entry = "Main_Page"; player->printColor("Type ^ywiki [entry]^x to look up a specific entry.\n\n"); } entry = entry.toLower(); entry.Replace(":", "_colon_"); if(!checkWinFilename(player->getSock(), entry.c_str())) return(0); sprintf(file, "%s/%s.txt", Path::Wiki, entry.c_str()); // If the file exists and was modified within the last hour, use the local cache if(!stat(file, &f_stat) && (time(0) - f_stat.st_mtim.tv_sec) < 3600) { viewFile(player->getSock(), file); return(0); } player->print("Loading entry...\n"); url << "mud.php?type=wiki&char=" << player->getName() << "&entry=" << entry; callWebserver(url.str(), true, true); return(0); } //********************************************************************* // wiki //********************************************************************* // This function allows a player to loop up a wiki entry bool WebInterface::wiki(bstring command, bstring tempBuf) { bstring::size_type pos=0; // we don't know the command yet if(command == "") { pos = tempBuf.Find(' '); if(pos == bstring::npos) command = tempBuf; else command = tempBuf.left(pos); } pos = tempBuf.Find(' '); if(pos == bstring::npos) { std::cout << "WebInterface: Wiki help failed; not enough data!" << std::endl; return(false); } bstring user = tempBuf.left(pos); user = user.toLower(); user.setAt(0, up(user.getAt(0))); tempBuf.erase(0, pos+1); // Clear out the user tempBuf = tempBuf.trim(); if( tempBuf == "" || strchr(tempBuf.c_str(), '/') != NULL || !checkWinFilename(0, tempBuf.c_str()) ) { std::cout << "WebInterface: Wiki help failed; invalid data" << std::endl; return(false); } std::cout << "WebInterface: Wiki help for user " << user << std::endl; const Player* player = gServer->findPlayer(user); if(!player) { outBuf += "That player is not logged on."; } else { char file[80]; sprintf(file, "%s/%s.txt", Path::Wiki, tempBuf.c_str()); viewFile(player->getSock(), file); } outBuf += EOT; return(true); }