diff options
Diffstat (limited to 'indra/newview/lllogchat.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/newview/lllogchat.cpp | 696 |
1 files changed, 515 insertions, 181 deletions
diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index ebb5912ace..90b169ecd3 100644..100755 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -25,14 +25,16 @@ */ #include "llviewerprecompiledheaders.h" - +#include "llfloaterconversationpreview.h" #include "llagent.h" #include "llagentui.h" +#include "llavatarnamecache.h" #include "lllogchat.h" #include "lltrans.h" #include "llviewercontrol.h" #include "lldiriterator.h" +#include "llfloaterimsessiontab.h" #include "llinstantmessage.h" #include "llsingleton.h" // for LLSingleton @@ -40,6 +42,7 @@ #include <boost/algorithm/string/replace.hpp> #include <boost/regex.hpp> #include <boost/regex/v4/match_results.hpp> +#include <boost/foreach.hpp> #if LL_MSVC #pragma warning(push) @@ -58,10 +61,11 @@ const S32 LOG_RECALL_SIZE = 2048; -const std::string IM_TIME("time"); -const std::string IM_TEXT("message"); -const std::string IM_FROM("from"); -const std::string IM_FROM_ID("from_id"); +const std::string LL_IM_TIME("time"); +const std::string LL_IM_TEXT("message"); +const std::string LL_IM_FROM("from"); +const std::string LL_IM_FROM_ID("from_id"); +const std::string LL_TRANSCRIPT_FILE_EXTENSION("txt"); const static std::string IM_SEPARATOR(": "); const static std::string NEW_LINE("\n"); @@ -84,6 +88,7 @@ const static std::string MULTI_LINE_PREFIX(" "); * Note: "You" was used as an avatar names in viewers of previous versions */ const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); +const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*"); /** * Regular expression suitable to match names like @@ -116,6 +121,15 @@ const static int IDX_TEXT = 3; using namespace boost::posix_time; using namespace boost::gregorian; +void append_to_last_message(std::list<LLSD>& messages, const std::string& line) +{ + if (!messages.size()) return; + + std::string im_text = messages.back()[LL_IM_TEXT].asString(); + im_text.append(line); + messages.back()[LL_IM_TEXT] = im_text; +} + class LLLogChatTimeScanner: public LLSingleton<LLLogChatTimeScanner> { public: @@ -191,15 +205,18 @@ private: std::stringstream mTimeStream; }; +LLLogChat::save_history_signal_t * LLLogChat::sSaveHistorySignal = NULL; +LLLoadHistoryThread::load_end_signal_t * LLLoadHistoryThread::mLoadEndSignal = NULL; + //static std::string LLLogChat::makeLogFileName(std::string filename) { /** - * Testing for in bound and out bound ad-hoc file names - * if it is then skip date stamping. - **/ - //LL_INFOS("") << "Befor:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ - boost::match_results<std::string::const_iterator> matches; + * Testing for in bound and out bound ad-hoc file names + * if it is then skip date stamping. + **/ + + boost::match_results<std::string::const_iterator> matches; bool inboundConf = boost::regex_match(filename, matches, INBOUND_CONFERENCE); bool outboundConf = boost::regex_match(filename, matches, OUTBOUND_CONFERENCE); if (!(inboundConf || outboundConf)) @@ -220,17 +237,17 @@ std::string LLLogChat::makeLogFileName(std::string filename) filename += dbuffer; } } - //LL_INFOS("") << "After:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ + filename = cleanFileName(filename); - filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename); - filename += ".txt"; - //LL_INFOS("") << "Full:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ + filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, filename); + filename += '.' + LL_TRANSCRIPT_FILE_EXTENSION; + return filename; } std::string LLLogChat::cleanFileName(std::string filename) { - std::string invalidChars = "\"\'\\/?*:.<>|[]{}~"; // Cannot match glob or illegal filename chars + std::string invalidChars = "\"\'\\/?*:.<>|[]{}~"; // Cannot match glob or illegal filename chars std::string::size_type position = filename.find_first_of(invalidChars); while (position != filename.npos) { @@ -242,27 +259,24 @@ std::string LLLogChat::cleanFileName(std::string filename) std::string LLLogChat::timestamp(bool withdate) { - time_t utc_time; - utc_time = time_corrected(); - std::string timeStr; - LLSD substitution; - substitution["datetime"] = (S32) utc_time; - if (withdate) { - timeStr = "["+LLTrans::getString ("TimeYear")+"]/[" - +LLTrans::getString ("TimeMonth")+"]/[" - +LLTrans::getString ("TimeDay")+"] [" - +LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"]"; + timeStr = "[" + LLTrans::getString ("TimeYear") + "]/[" + + LLTrans::getString ("TimeMonth") + "]/[" + + LLTrans::getString ("TimeDay") + "] [" + + LLTrans::getString ("TimeHour") + "]:[" + + LLTrans::getString ("TimeMin") + "]"; } else { timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin")+"]"; + + LLTrans::getString ("TimeMin")+"]"; } + LLSD substitution; + substitution["datetime"] = (S32)time_corrected(); + LLStringUtil::format (timeStr, substitution); return timeStr; } @@ -270,9 +284,9 @@ std::string LLLogChat::timestamp(bool withdate) //static void LLLogChat::saveHistory(const std::string& filename, - const std::string& from, - const LLUUID& from_id, - const std::string& line) + const std::string& from, + const LLUUID& from_id, + const std::string& line) { std::string tmp_filename = filename; LLStringUtil::trim(tmp_filename); @@ -312,156 +326,387 @@ void LLLogChat::saveHistory(const std::string& filename, file << LLChatLogFormatter(item) << std::endl; file.close(); + + if (NULL != sSaveHistorySignal) + { + (*sSaveHistorySignal)(); + } } -void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLineType, const LLSD&, void*), void* userdata) +// static +void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params) { - if(!filename.size()) + if (file_name.empty()) + { + LL_WARNS("LLLogChat::loadChatHistory") << "Session name is Empty!" << LL_ENDL; + return ; + } + + bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false; + + LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + return; //No previous conversation with this name. + } + } + + char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ + char *bptr; + S32 len; + bool firstline = TRUE; + + if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) + { //We need to load the whole historyFile or it's smaller than recall size, so get it all. + firstline = FALSE; + if (fseek(fptr, 0, SEEK_SET)) + { + fclose(fptr); + return; + } + } + while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr)) + { + len = strlen(buffer) - 1; /*Flawfinder: ignore*/ + for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; + + if (firstline) + { + firstline = FALSE; + continue; + } + + std::string line(buffer); + + //updated 1.23 plain text log format requires a space added before subsequent lines in a multilined message + if (' ' == line[0]) + { + line.erase(0, MULTI_LINE_PREFIX.length()); + append_to_last_message(messages, '\n' + line); + } + else if (0 == len && ('\n' == line[0] || '\r' == line[0])) + { + //to support old format's multilined messages with new lines used to divide paragraphs + append_to_last_message(messages, line); + } + else + { + LLSD item; + if (!LLChatLogParser::parse(line, item, load_params)) + { + item[LL_IM_TEXT] = line; + } + messages.push_back(item); + } + } + fclose(fptr); + + +} + +void LLLogChat::startChatHistoryThread(const std::string& file_name, const LLSD& load_params) +{ + + LLLoadHistoryThread* mThread = new LLLoadHistoryThread(); + mThread->start(); + mThread->setHistoryParams(file_name, load_params); +} +// static +std::string LLLogChat::oldLogFileName(std::string filename) +{ + // get Users log directory + std::string directory = gDirUtilp->getPerAccountChatLogsDir(); + + // add final OS dependent delimiter + directory += gDirUtilp->getDirDelimiter(); + + // lest make sure the file name has no invalid characters before making the pattern + filename = cleanFileName(filename); + + // create search pattern + std::string pattern = filename + ( filename == "chat" ? "-???\?-?\?-??.txt" : "-???\?-??.txt"); + + std::vector<std::string> allfiles; + LLDirIterator iter(directory, pattern); + std::string scanResult; + + while (iter.next(scanResult)) { - llwarns << "Filename is Empty!" << llendl; - return ; + allfiles.push_back(scanResult); } - - LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "r"); /*Flawfinder: ignore*/ - if (!fptr) + + if (allfiles.size() == 0) // if no result from date search, return generic filename { - callback(LOG_EMPTY, LLSD(), userdata); - return; //No previous conversation with this name. + scanResult = directory + filename + '.' + LL_TRANSCRIPT_FILE_EXTENSION; } - else + else { - char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ - char *bptr; - S32 len; - bool firstline=TRUE; + sort(allfiles.begin(), allfiles.end()); + scanResult = directory + allfiles.back(); + // this file is now the most recent version of the file. + } + + return scanResult; +} + +// static +void LLLogChat::findTranscriptFiles(std::string pattern, std::vector<std::string>& list_of_transcriptions) +{ + // get Users log directory + std::string dirname = gDirUtilp->getPerAccountChatLogsDir(); + + // add final OS dependent delimiter + dirname += gDirUtilp->getDirDelimiter(); - if ( fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END) ) - { //File is smaller than recall size. Get it all. - firstline = FALSE; - if ( fseek(fptr, 0, SEEK_SET) ) + LLDirIterator iter(dirname, pattern); + std::string filename; + while (iter.next(filename)) + { + std::string fullname = gDirUtilp->add(dirname, filename); + + LLFILE * filep = LLFile::fopen(fullname, "rb"); + if (NULL != filep) + { + if(makeLogFileName("chat")== fullname) { - fclose(fptr); + //Add Nearby chat history to the list of transcriptions + list_of_transcriptions.push_back(gDirUtilp->add(dirname, filename)); + LLFile::close(filep); return; } - } + char buffer[LOG_RECALL_SIZE]; - while ( fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr) ) - { - len = strlen(buffer) - 1; /*Flawfinder: ignore*/ - for ( bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; - - if (!firstline) + fseek(filep, 0, SEEK_END); // seek to end of file + S32 bytes_to_read = ftell(filep); // get current file pointer + fseek(filep, 0, SEEK_SET); // seek back to beginning of file + + // limit the number characters to read from file + if (bytes_to_read >= LOG_RECALL_SIZE) { - LLSD item; - std::string line(buffer); - std::istringstream iss(line); - - if (!LLChatLogParser::parse(line, item)) - { - item["message"] = line; - callback(LOG_LINE, item, userdata); - } - else - { - callback(LOG_LLSD, item, userdata); - } + bytes_to_read = LOG_RECALL_SIZE - 1; } - else + + if (bytes_to_read > 0 && NULL != fgets(buffer, bytes_to_read, filep)) { - firstline = FALSE; + //matching a timestamp + boost::match_results<std::string::const_iterator> matches; + if (boost::regex_match(std::string(buffer), matches, TIMESTAMP)) + { + list_of_transcriptions.push_back(gDirUtilp->add(dirname, filename)); + } } + LLFile::close(filep); } - callback(LOG_END, LLSD(), userdata); - - fclose(fptr); } } -void append_to_last_message(std::list<LLSD>& messages, const std::string& line) +// static +void LLLogChat::getListOfTranscriptFiles(std::vector<std::string>& list_of_transcriptions) { - if (!messages.size()) return; - - std::string im_text = messages.back()[IM_TEXT].asString(); - im_text.append(line); - messages.back()[IM_TEXT] = im_text; + // create search pattern + std::string pattern = "*." + LL_TRANSCRIPT_FILE_EXTENSION; + findTranscriptFiles(pattern, list_of_transcriptions); } // static -void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& messages) +void LLLogChat::getListOfTranscriptBackupFiles(std::vector<std::string>& list_of_transcriptions) { - if (file_name.empty()) + // create search pattern + std::string pattern = "*." + LL_TRANSCRIPT_FILE_EXTENSION + ".backup*"; + findTranscriptFiles(pattern, list_of_transcriptions); +} + +//static +boost::signals2::connection LLLogChat::setSaveHistorySignal(const save_history_signal_t::slot_type& cb) +{ + if (NULL == sSaveHistorySignal) { - llwarns << "Session name is Empty!" << llendl; - return ; - } - //LL_INFOS("") << "Loading:" << file_name << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ - //LL_INFOS("") << "Current:" << makeLogFileName(file_name) << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ - LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ - if (!fptr) - { - fptr = LLFile::fopen(oldLogFileName(file_name), "r");/*Flawfinder: ignore*/ - if (!fptr) - { - if (!fptr) return; //No previous conversation with this name. - } - } - - //LL_INFOS("") << "Reading:" << file_name << LL_ENDL; - char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ - char *bptr; - S32 len; - bool firstline = TRUE; - - if (fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) - { //File is smaller than recall size. Get it all. - firstline = FALSE; - if (fseek(fptr, 0, SEEK_SET)) - { - fclose(fptr); - return; - } + sSaveHistorySignal = new save_history_signal_t(); } - while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr)) + return sSaveHistorySignal->connect(cb); +} + +//static +bool LLLogChat::moveTranscripts(const std::string originDirectory, + const std::string targetDirectory, + std::vector<std::string>& listOfFilesToMove, + std::vector<std::string>& listOfFilesMoved) +{ + std::string newFullPath; + bool movedAllTranscripts = true; + std::string backupFileName; + unsigned backupFileCount; + + BOOST_FOREACH(const std::string& fullpath, listOfFilesToMove) { - len = strlen(buffer) - 1; /*Flawfinder: ignore*/ - for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; - - if (firstline) + backupFileCount = 0; + newFullPath = targetDirectory + fullpath.substr(originDirectory.length(), std::string::npos); + + //The target directory contains that file already, so lets store it + if(LLFile::isfile(newFullPath)) { - firstline = FALSE; - continue; + backupFileName = newFullPath + ".backup"; + + //If needed store backup file as .backup1 etc. + while(LLFile::isfile(backupFileName)) + { + ++backupFileCount; + backupFileName = newFullPath + ".backup" + boost::lexical_cast<std::string>(backupFileCount); + } + + //Rename the file to its backup name so it is not overwritten + LLFile::rename(newFullPath, backupFileName); + } + + S32 retry_count = 0; + while (retry_count < 5) + { + //success is zero + if (LLFile::rename(fullpath, newFullPath) != 0) + { + retry_count++; + S32 result = errno; + LL_WARNS("LLLogChat::moveTranscripts") << "Problem renaming " << fullpath << " - errorcode: " + << result << " attempt " << retry_count << LL_ENDL; + + ms_sleep(100); + } + else + { + listOfFilesMoved.push_back(newFullPath); + + if (retry_count) + { + LL_WARNS("LLLogChat::moveTranscripts") << "Successfully renamed " << fullpath << LL_ENDL; + } + break; + } } + } + + if(listOfFilesMoved.size() != listOfFilesToMove.size()) + { + movedAllTranscripts = false; + } - std::string line(buffer); + return movedAllTranscripts; +} + +//static +bool LLLogChat::moveTranscripts(const std::string currentDirectory, + const std::string newDirectory, + std::vector<std::string>& listOfFilesToMove) +{ + std::vector<std::string> listOfFilesMoved; + return moveTranscripts(currentDirectory, newDirectory, listOfFilesToMove, listOfFilesMoved); +} + +//static +void LLLogChat::deleteTranscripts() +{ + std::vector<std::string> list_of_transcriptions; + getListOfTranscriptFiles(list_of_transcriptions); + getListOfTranscriptBackupFiles(list_of_transcriptions); - //updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message - if (' ' == line[0]) + BOOST_FOREACH(const std::string& fullpath, list_of_transcriptions) + { + S32 retry_count = 0; + while (retry_count < 5) { - line.erase(0, MULTI_LINE_PREFIX.length()); - append_to_last_message(messages, '\n' + line); + if (0 != LLFile::remove(fullpath)) + { + retry_count++; + S32 result = errno; + LL_WARNS("LLLogChat::deleteTranscripts") << "Problem removing " << fullpath << " - errorcode: " + << result << " attempt " << retry_count << LL_ENDL; + + if(retry_count >= 5) + { + LL_WARNS("LLLogChat::deleteTranscripts") << "Failed to remove " << fullpath << LL_ENDL; + return; + } + + ms_sleep(100); + } + else + { + if (retry_count) + { + LL_WARNS("LLLogChat::deleteTranscripts") << "Successfully removed " << fullpath << LL_ENDL; + } + break; + } } - else if (0 == len && ('\n' == line[0] || '\r' == line[0])) + } + + LLFloaterIMSessionTab::processChatHistoryStyleUpdate(true); +} + +// static +bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id, bool is_group) +{ + std::vector<std::string> list_of_transcriptions; + LLLogChat::getListOfTranscriptFiles(list_of_transcriptions); + + if (list_of_transcriptions.size() > 0) + { + LLAvatarName avatar_name; + LLAvatarNameCache::get(avatar_id, &avatar_name); + std::string avatar_user_name = avatar_name.getAccountName(); + if(!is_group) { - //to support old format's multilined messages with new lines used to divide paragraphs - append_to_last_message(messages, line); + std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_'); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + { + if (std::string::npos != transcript_file_name.find(avatar_user_name)) + { + return true; + } + } } else { - LLSD item; - if (!LLChatLogParser::parse(line, item)) + std::string file_name; + gCacheName->getGroupName(avatar_id, file_name); + file_name = makeLogFileName(file_name); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) { - item[IM_TEXT] = line; + if (transcript_file_name == file_name) + { + return true; + } } - messages.push_back(item); } + } - fclose(fptr); + + return false; +} + +bool LLLogChat::isNearbyTranscriptExist() +{ + std::vector<std::string> list_of_transcriptions; + LLLogChat::getListOfTranscriptFiles(list_of_transcriptions); + + std::string file_name; + file_name = makeLogFileName("chat"); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + { + if (transcript_file_name == file_name) + { + return true; + } + } + return false; } //*TODO mark object's names in a special way so that they will be distinguishable form avatar name //which are more strict by its nature (only firstname and secondname) -//Example, an object's name can be writen like "Object <actual_object's_name>" +//Example, an object's name can be written like "Object <actual_object's_name>" void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const { if (!im.isMap()) @@ -470,19 +715,19 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const return; } - if (im[IM_TIME].isDefined()) + if (im[LL_IM_TIME].isDefined()) { - std::string timestamp = im[IM_TIME].asString(); + std::string timestamp = im[LL_IM_TIME].asString(); boost::trim(timestamp); ostr << '[' << timestamp << ']' << TWO_SPACES; } //*TODO mark object's names in a special way so that they will be distinguishable form avatar name //which are more strict by its nature (only firstname and secondname) - //Example, an object's name can be writen like "Object <actual_object's_name>" - if (im[IM_FROM].isDefined()) + //Example, an object's name can be written like "Object <actual_object's_name>" + if (im[LL_IM_FROM].isDefined()) { - std::string from = im[IM_FROM].asString(); + std::string from = im[LL_IM_FROM].asString(); boost::trim(from); if (from.size()) { @@ -490,9 +735,9 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const } } - if (im[IM_TEXT].isDefined()) + if (im[LL_IM_TEXT].isDefined()) { - std::string im_text = im[IM_TEXT].asString(); + std::string im_text = im[LL_IM_TEXT].asString(); //multilined text will be saved with prepended spaces boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX); @@ -500,10 +745,11 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const } } -bool LLChatLogParser::parse(std::string& raw, LLSD& im) +bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params) { if (!raw.length()) return false; + bool cut_off_todays_date = parse_params.has("cut_off_todays_date") ? parse_params["cut_off_todays_date"].asBoolean() : true; im = LLSD::emptyMap(); //matching a timestamp @@ -518,13 +764,18 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) boost::trim(timestamp); timestamp.erase(0, 1); timestamp.erase(timestamp.length()-1, 1); - LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp); - im[IM_TIME] = timestamp; + + if (cut_off_todays_date) + { + LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp); + } + + im[LL_IM_TIME] = timestamp; } else { //timestamp is optional - im[IM_TIME] = ""; + im[LL_IM_TIME] = ""; } bool has_stuff = matches[IDX_STUFF].matched; @@ -550,8 +801,8 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) if (!has_name || name == SYSTEM_FROM) { //name is optional too - im[IM_FROM] = SYSTEM_FROM; - im[IM_FROM_ID] = LLUUID::null; + im[LL_IM_FROM] = SYSTEM_FROM; + im[LL_IM_FROM_ID] = LLUUID::null; } //possibly a case of complex object names consisting of 3+ words @@ -560,8 +811,8 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) U32 divider_pos = stuff.find(NAME_TEXT_DIVIDER); if (divider_pos != std::string::npos && divider_pos < (stuff.length() - NAME_TEXT_DIVIDER.length())) { - im[IM_FROM] = stuff.substr(0, divider_pos); - im[IM_TEXT] = stuff.substr(divider_pos + NAME_TEXT_DIVIDER.length()); + im[LL_IM_FROM] = stuff.substr(0, divider_pos); + im[LL_IM_TEXT] = stuff.substr(divider_pos + NAME_TEXT_DIVIDER.length()); return true; } } @@ -569,7 +820,7 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) if (!has_name) { //text is mandatory - im[IM_TEXT] = stuff; + im[LL_IM_TEXT] = stuff; return true; //parse as a message from Second Life } @@ -581,45 +832,128 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) { std::string agent_name; LLAgentUI::buildFullname(agent_name); - im[IM_FROM] = agent_name; - im[IM_FROM_ID] = gAgentID; + im[LL_IM_FROM] = agent_name; + im[LL_IM_FROM_ID] = gAgentID; } else { - im[IM_FROM] = name; + im[LL_IM_FROM] = name; } - im[IM_TEXT] = name_and_text[IDX_TEXT]; + im[LL_IM_TEXT] = name_and_text[IDX_TEXT]; return true; //parsed name and message text, maybe have a timestamp too } -std::string LLLogChat::oldLogFileName(std::string filename) -{ - std::string scanResult; - std::string directory = gDirUtilp->getPerAccountChatLogsDir();/* get Users log directory */ - directory += gDirUtilp->getDirDelimiter();/* add final OS dependent delimiter */ - filename=cleanFileName(filename);/* lest make shure the file name has no invalad charecters befor making the pattern */ - std::string pattern = (filename+(( filename == "chat" ) ? "-???\?-?\?-??.txt" : "-???\?-??.txt"));/* create search pattern*/ - //LL_INFOS("") << "Checking:" << directory << " for " << pattern << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ - std::vector<std::string> allfiles; - LLDirIterator iter(directory, pattern); - while (iter.next(scanResult)) - { - //LL_INFOS("") << "Found :" << scanResult << LL_ENDL; - allfiles.push_back(scanResult); - } - - if (allfiles.size() == 0) // if no result from date search, return generic filename - { - scanResult = directory + filename + ".txt"; - } - else - { - std::sort(allfiles.begin(), allfiles.end()); - scanResult = directory + allfiles.back(); - // thisfile is now the most recent version of the file. - } - //LL_INFOS("") << "Reading:" << scanResult << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ - return scanResult; -} + + + LLLoadHistoryThread::LLLoadHistoryThread() : LLThread("load chat history") + { + mNewLoad = false; + } + + void LLLoadHistoryThread::run() + { + while (!LLApp::isQuitting()) + { + if(mNewLoad) + { + loadHistory(mFileName,mMessages,mLoadParams); + shutdown(); + } + } + } + void LLLoadHistoryThread::setHistoryParams(const std::string& file_name, const LLSD& load_params) + { + mFileName = file_name; + mLoadParams = load_params; + mNewLoad = true; + } + void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params) + { + + if (file_name.empty()) + { + LL_WARNS("LLLogChat::loadHistory") << "Session name is Empty!" << LL_ENDL; + return ; + } + + bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false; + + LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + mNewLoad = false; + (*mLoadEndSignal)(messages, file_name); + return; //No previous conversation with this name. + } + } + + char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ + char *bptr; + S32 len; + bool firstline = TRUE; + + if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) + { //We need to load the whole historyFile or it's smaller than recall size, so get it all. + firstline = FALSE; + if (fseek(fptr, 0, SEEK_SET)) + { + fclose(fptr); + mNewLoad = false; + (*mLoadEndSignal)(messages, file_name); + return; + } + } + while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr)) + { + len = strlen(buffer) - 1; /*Flawfinder: ignore*/ + for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; + + if (firstline) + { + firstline = FALSE; + continue; + } + + std::string line(buffer); + + //updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message + if (' ' == line[0]) + { + line.erase(0, MULTI_LINE_PREFIX.length()); + append_to_last_message(messages, '\n' + line); + } + else if (0 == len && ('\n' == line[0] || '\r' == line[0])) + { + //to support old format's multilined messages with new lines used to divide paragraphs + append_to_last_message(messages, line); + } + else + { + LLSD item; + if (!LLChatLogParser::parse(line, item, load_params)) + { + item[LL_IM_TEXT] = line; + } + messages.push_back(item); + } + } + fclose(fptr); + mNewLoad = false; + (*mLoadEndSignal)(messages, file_name); + } + + //static + boost::signals2::connection LLLoadHistoryThread::setLoadEndSignal(const load_end_signal_t::slot_type& cb) + { + if (NULL == mLoadEndSignal) + { + mLoadEndSignal = new load_end_signal_t(); + } + + return mLoadEndSignal->connect(cb); + } |