From d6ea42984553b7adb6f26cf2ed094d32e36814d2 Mon Sep 17 00:00:00 2001 From: James Cook Date: Tue, 25 May 2010 11:40:29 -0700 Subject: DEV-50013 Display names load at startup in local chat/IM history Had to change the chat log file format to include agent_id. Code will load viewer 2.0 logs, but viewer 2.0 will just discard data from 2.1 logs, which seems OK. Reviewed with Leyla. --- indra/newview/lllogchat.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'indra/newview/lllogchat.cpp') diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index be8b2363ad..1d348834fe 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -426,6 +426,12 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const return; } + if (im[IM_FROM_ID].isDefined()) + { + LLUUID from_id = im[IM_FROM_ID].asUUID(); + ostr << '{' << from_id.asString() << '}'; + } + if (im[IM_TIME].isDefined()) { std::string timestamp = im[IM_TIME].asString(); @@ -456,15 +462,32 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const } } -bool LLChatLogParser::parse(std::string& raw, LLSD& im) +bool LLChatLogParser::parse(const std::string& raw, LLSD& im) { if (!raw.length()) return false; im = LLSD::emptyMap(); + // In Viewer 2.1 we added UUID to chat/IM logging so we can look up + // display names + std::string line = raw; + if (raw[0] == '{') + { + const S32 UUID_LEN = 36; + size_t pos = line.find_first_of('}'); + // If it matches, pos will be 37 + if (pos != line.npos && pos > UUID_LEN) + { + std::string uuid_string = line.substr(1, UUID_LEN); + LLUUID from_id(uuid_string); + im[IM_FROM_ID] = from_id; + line = line.substr(pos + 1); + } + } + //matching a timestamp boost::match_results matches; - if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) return false; + if (!boost::regex_match(line, matches, TIMESTAMP_AND_STUFF)) return false; bool has_timestamp = matches[IDX_TIMESTAMP].matched; if (has_timestamp) -- cgit v1.3 From b34eee98d1cd542777a9fc28b1e1d2e3a0de24e5 Mon Sep 17 00:00:00 2001 From: James Cook Date: Wed, 2 Jun 2010 15:21:46 -0700 Subject: DEV-50451 Fix names in local chat history loading, again Switch to new serialization format, change filename so old viewers don't get confused if the user switches back. Reviewed with Richard --- indra/newview/llimview.cpp | 5 -- indra/newview/lllogchat.cpp | 179 ++++++++++------------------------------- indra/newview/lllogchat.h | 47 ++++------- indra/newview/llnearbychat.cpp | 17 ++-- 4 files changed, 64 insertions(+), 184 deletions(-) (limited to 'indra/newview/lllogchat.cpp') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 00e7537b8b..d0bb98fc8c 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -71,11 +71,6 @@ #include "llviewerparcelmgr.h" -const static std::string IM_TIME("time"); -const static std::string IM_TEXT("message"); -const static std::string IM_FROM("from"); -const static std::string IM_FROM_ID("from_id"); - const static std::string NO_SESSION("(IM Session Doesn't Exist)"); const static std::string ADHOC_NAME_SUFFIX(" Conference"); diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 1d348834fe..d4ebdd768b 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -32,13 +32,18 @@ #include "llviewerprecompiledheaders.h" +#include "lllogchat.h" + +// viewer includes #include "llagent.h" #include "llagentui.h" -#include "lllogchat.h" #include "lltrans.h" #include "llviewercontrol.h" +// library includes +#include "llchat.h" #include "llinstantmessage.h" +#include "llsdserialize.h" #include "llsingleton.h" // for LLSingleton #include @@ -62,12 +67,13 @@ const S32 LOG_RECALL_SIZE = 2048; -const static std::string IM_TIME("time"); -const static std::string IM_TEXT("message"); -const static std::string IM_FROM("from"); -const static std::string IM_FROM_ID("from_id"); -const static std::string IM_SEPARATOR(": "); +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 IM_SOURCE_TYPE("source_type"); +const static std::string IM_SEPARATOR(": "); const static std::string NEW_LINE("\n"); const static std::string NEW_LINE_SPACE_PREFIX("\n "); const static std::string TWO_SPACES(" "); @@ -190,7 +196,8 @@ std::string LLLogChat::makeLogFileName(std::string filename) { filename = cleanFileName(filename); filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename); - filename += ".txt"; + // new files are llsd notation format + filename += ".llsd"; return filename; } @@ -239,6 +246,18 @@ void LLLogChat::saveHistory(const std::string& filename, const std::string& from, const LLUUID& from_id, const std::string& line) +{ + LLChat chat; + chat.mText = line; + chat.mFromName = from; + chat.mFromID = from_id; + // default to being from an agent + chat.mSourceType = CHAT_SOURCE_AGENT; + saveHistory(filename, chat); +} + +//static +void LLLogChat::saveHistory(const std::string& filename, const LLChat& chat) { std::string tmp_filename = filename; LLStringUtil::trim(tmp_filename); @@ -260,89 +279,27 @@ void LLLogChat::saveHistory(const std::string& filename, LLSD item; if (gSavedPerAccountSettings.getBOOL("LogTimestamp")) - item["time"] = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")); + item[IM_TIME] = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")); - item["from_id"] = from_id; - item["message"] = line; + item[IM_FROM_ID] = chat.mFromID; + item[IM_TEXT] = chat.mText; + item[IM_SOURCE_TYPE] = chat.mSourceType; //adding "Second Life:" for all system messages to make chat log history parsing more reliable - if (from.empty() && from_id.isNull()) + if (chat.mFromName.empty() && chat.mFromID.isNull()) { - item["from"] = SYSTEM_FROM; + item[IM_FROM] = SYSTEM_FROM; } else { - item["from"] = from; + item[IM_FROM] = chat.mFromName; } - file << LLChatLogFormatter(item) << std::endl; + file << LLSDOStreamer(item) << std::endl; file.close(); } -void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLineType, const LLSD&, void*), void* userdata) -{ - if(!filename.size()) - { - llwarns << "Filename is Empty!" << llendl; - return ; - } - - LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "r"); /*Flawfinder: ignore*/ - if (!fptr) - { - callback(LOG_EMPTY, LLSD(), userdata); - return; //No previous conversation with this name. - } - else - { - 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; - } - } - - 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) - { - 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); - } - } - else - { - firstline = FALSE; - } - } - callback(LOG_END, LLSD(), userdata); - - fclose(fptr); - } -} - void append_to_last_message(std::list& messages, const std::string& line) { if (!messages.size()) return; @@ -352,6 +309,7 @@ void append_to_last_message(std::list& messages, const std::string& line) messages.back()[IM_TEXT] = im_text; } +// static void LLLogChat::loadAllHistory(const std::string& file_name, std::list& messages) { if (file_name.empty()) @@ -415,53 +373,7 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list& me fclose(fptr); } -//*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 " -void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const -{ - if (!im.isMap()) - { - llwarning("invalid LLSD type of an instant message", 0); - return; - } - - if (im[IM_FROM_ID].isDefined()) - { - LLUUID from_id = im[IM_FROM_ID].asUUID(); - ostr << '{' << from_id.asString() << '}'; - } - - if (im[IM_TIME].isDefined()) - { - std::string timestamp = im[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 " - if (im[IM_FROM].isDefined()) - { - std::string from = im[IM_FROM].asString(); - boost::trim(from); - if (from.size()) - { - ostr << from << IM_SEPARATOR; - } - } - - if (im[IM_TEXT].isDefined()) - { - std::string im_text = im[IM_TEXT].asString(); - - //multilined text will be saved with prepended spaces - boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX); - ostr << im_text; - } -} - +// static bool LLChatLogParser::parse(const std::string& raw, LLSD& im) { if (!raw.length()) return false; @@ -470,24 +382,19 @@ bool LLChatLogParser::parse(const std::string& raw, LLSD& im) // In Viewer 2.1 we added UUID to chat/IM logging so we can look up // display names - std::string line = raw; if (raw[0] == '{') { - const S32 UUID_LEN = 36; - size_t pos = line.find_first_of('}'); - // If it matches, pos will be 37 - if (pos != line.npos && pos > UUID_LEN) - { - std::string uuid_string = line.substr(1, UUID_LEN); - LLUUID from_id(uuid_string); - im[IM_FROM_ID] = from_id; - line = line.substr(pos + 1); - } + // ...this is a viewer 2.1, new-style LLSD notation format log + std::istringstream raw_stream(raw); + LLPointer parser = new LLSDNotationParser(); + S32 count = parser->parse(raw_stream, im, raw.length()); + // expect several map items per parsed line + return (count != LLSDParser::PARSE_FAILURE); } //matching a timestamp boost::match_results matches; - if (!boost::regex_match(line, matches, TIMESTAMP_AND_STUFF)) return false; + if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) return false; bool has_timestamp = matches[IDX_TIMESTAMP].matched; if (has_timestamp) diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index a67b58e55b..674f27407e 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -30,10 +30,11 @@ * $/LicenseInfo$ */ - #ifndef LL_LLLOGCHAT_H #define LL_LLLOGCHAT_H +class LLChat; + class LLLogChat { public: @@ -46,48 +47,21 @@ public: }; static std::string timestamp(bool withdate = false); static std::string makeLogFileName(std::string(filename)); + + // Log a single line item to the appropriate chat file + static void saveHistory(const std::string& filename, const LLChat& chat); + + // Prefer the above version - it saves more metadata about the item static void saveHistory(const std::string& filename, const std::string& from, const LLUUID& from_id, const std::string& line); - /** @deprecated @see loadAllHistory() */ - static void loadHistory(const std::string& filename, - void (*callback)(ELogLineType, const LLSD&, void*), - void* userdata); - static void loadAllHistory(const std::string& file_name, std::list& messages); private: static std::string cleanFileName(std::string filename); }; -/** - * Formatter for the plain text chat log files - */ -class LLChatLogFormatter -{ -public: - LLChatLogFormatter(const LLSD& im) : mIM(im) {} - virtual ~LLChatLogFormatter() {}; - - friend std::ostream& operator<<(std::ostream& str, const LLChatLogFormatter& formatter) - { - formatter.format(formatter.mIM, str); - return str; - } - -protected: - - /** - * Format an instant message to a stream - * Timestamps and sender names are required - * New lines of multilined messages are prepended with a space - */ - void format(const LLSD& im, std::ostream& ostr) const; - - LLSD mIM; -}; - /** * Parser for the plain text chat log files */ @@ -113,4 +87,11 @@ protected: virtual ~LLChatLogParser() {}; }; +// LLSD map lookup constants +extern const std::string IM_TIME; //("time"); +extern const std::string IM_TEXT; //("message"); +extern const std::string IM_FROM; //("from"); +extern const std::string IM_FROM_ID; //("from_id"); +extern const std::string IM_SOURCE_TYPE; //("source_type"); + #endif diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 74ede67c97..631d1fcac7 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -62,12 +62,6 @@ static const S32 RESIZE_BAR_THICKNESS = 3; -const static std::string IM_TIME("time"); -const static std::string IM_TEXT("message"); -const static std::string IM_FROM("from"); -const static std::string IM_FROM_ID("from_id"); - - LLNearbyChat::LLNearbyChat(const LLSD& key) : LLDockableFloater(NULL, false, false, key) ,mChatHistory(NULL) @@ -211,7 +205,7 @@ void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) if (gSavedPerAccountSettings.getBOOL("LogNearbyChat")) { - LLLogChat::saveHistory("chat", chat.mFromName, chat.mFromID, chat.mText); + LLLogChat::saveHistory("chat", chat); } } @@ -318,11 +312,14 @@ void LLNearbyChat::loadHistory() chat.mTimeStr = msg[IM_TIME].asString(); chat.mChatStyle = CHAT_STYLE_HISTORY; - chat.mSourceType = CHAT_SOURCE_AGENT; - if (from_id.isNull() && SYSTEM_FROM == from) + if (msg.has(IM_SOURCE_TYPE)) + { + S32 source_type = msg[IM_SOURCE_TYPE].asInteger(); + chat.mSourceType = (EChatSourceType)source_type; + } + else if (from_id.isNull() && SYSTEM_FROM == from) { chat.mSourceType = CHAT_SOURCE_SYSTEM; - } else if (from_id.isNull()) { -- cgit v1.3 From cb5d8d1a9295076327f23e5f6d6c91fd0d4580ea Mon Sep 17 00:00:00 2001 From: Leyla Farazha Date: Fri, 22 Oct 2010 17:41:06 -0700 Subject: DN-181 Chat & IM logs saved in unreadable .llsd instead of .txt --- indra/llmessage/llcachename.cpp | 27 +++++++ indra/llmessage/llcachename.h | 6 ++ indra/newview/llchathistory.cpp | 47 ++++++++++-- indra/newview/llimview.cpp | 30 ++++++-- indra/newview/lllogchat.cpp | 157 +++++++++++++++++++++++++++++----------- indra/newview/lllogchat.h | 40 ++++++++-- indra/newview/llnearbychat.cpp | 56 ++++++++++---- 7 files changed, 287 insertions(+), 76 deletions(-) (limited to 'indra/newview/lllogchat.cpp') diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index 4a66a31c35..522b99bc02 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -38,6 +38,8 @@ #include "message.h" #include "llmemtype.h" +#include + // llsd serialization constants static const std::string AGENTS("agents"); static const std::string GROUPS("groups"); @@ -551,6 +553,31 @@ std::string LLCacheName::buildUsername(const std::string& full_name) return full_name; } +//static +std::string LLCacheName::buildLegacyName(const std::string& complete_name) +{ + boost::regex complete_name_regex("(.+)( \\()([A-Za-z]+)(.[A-Za-z]+)*(\\))"); + boost::match_results name_results; + if (!boost::regex_match(complete_name, name_results, complete_name_regex)) return complete_name; + + std::string legacy_name = name_results[3]; + // capitalize the first letter + std::string cap_letter = legacy_name.substr(0, 1); + LLStringUtil::toUpper(cap_letter); + legacy_name = cap_letter + legacy_name.substr(1); + + if (name_results[4].matched) + { + std::string last_name = name_results[4]; + std::string cap_letter = last_name.substr(1, 1); + LLStringUtil::toUpper(cap_letter); + last_name = cap_letter + last_name.substr(2); + legacy_name = legacy_name + " " + last_name; + } + + return legacy_name; +} + // This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer. // The reason it is a slot is so that the legacy get() function below can bind an old callback // and pass it as a slot. The reason it isn't a boost::function is so that trackable behavior diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h index b469803060..b108e37157 100644 --- a/indra/llmessage/llcachename.h +++ b/indra/llmessage/llcachename.h @@ -90,6 +90,12 @@ public: // "Random Linden" -> "random.linden" static std::string buildUsername(const std::string& name); + // Converts a complete display name to a legacy name + // if possible, otherwise returns the input + // "Alias (random.linden)" -> "Random Linden" + // "Something random" -> "Something random" + static std::string buildLegacyName(const std::string& name); + // If available, this method copies the group name into the string // provided. The caller must allocate at least // DB_GROUP_NAME_BUF_SIZE characters. If not available, this diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 378c4358b3..cb5cf4a61d 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -54,6 +54,7 @@ #include "llviewertexteditor.h" #include "llworld.h" #include "lluiconstants.h" +#include "llstring.h" #include "llviewercontrol.h" @@ -260,7 +261,7 @@ public: if((chat.mFromID.isNull() && chat.mFromName.empty()) || chat.mFromName == SYSTEM_FROM && chat.mFromID.isNull()) { mSourceType = CHAT_SOURCE_SYSTEM; - } + } mUserNameFont = style_params.font(); LLTextBox* user_name = getChild("user_name"); @@ -268,14 +269,14 @@ public: user_name->setColor(style_params.color()); if (chat.mFromName.empty() - || mSourceType == CHAT_SOURCE_SYSTEM - || mAvatarID.isNull()) + || mSourceType == CHAT_SOURCE_SYSTEM) { mFrom = LLTrans::getString("SECOND_LIFE"); user_name->setValue(mFrom); updateMinUserNameWidth(); } else if (mSourceType == CHAT_SOURCE_AGENT + && !mAvatarID.isNull() && chat.mChatStyle != CHAT_STYLE_HISTORY) { // ...from a normal user, lookup the name and fill in later. @@ -288,7 +289,41 @@ public: LLAvatarNameCache::get(mAvatarID, boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2)); } - else { + else if (chat.mChatStyle == CHAT_STYLE_HISTORY || + mSourceType == CHAT_SOURCE_AGENT) + { + //if it's an avatar name with a username add formatting + S32 username_start = chat.mFromName.rfind(" ("); + S32 username_end = chat.mFromName.rfind(')'); + + if (username_start != std::string::npos && + username_end == (chat.mFromName.length() - 1)) + { + mFrom = chat.mFromName.substr(0, username_start); + user_name->setValue(mFrom); + + if (gSavedSettings.getBOOL("NameTagShowUsernames")) + { + std::string username = chat.mFromName.substr(username_start + 2); + username = username.substr(0, username.length() - 1); + LLStyle::Params style_params_name; + LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); + style_params_name.color(userNameColor); + style_params_name.font.name("SansSerifSmall"); + style_params_name.font.style("NORMAL"); + style_params_name.readonly_color(userNameColor); + user_name->appendText(" - " + username, FALSE, style_params_name); + } + } + else + { + mFrom = chat.mFromName; + user_name->setValue(mFrom); + updateMinUserNameWidth(); + } + } + else + { // ...from an object, just use name as given mFrom = chat.mFromName; user_name->setValue(mFrom); @@ -367,7 +402,9 @@ public: user_name->setValue( LLSD(av_name.mDisplayName ) ); user_name->setToolTip( av_name.mUsername ); - if (gSavedSettings.getBOOL("NameTagShowUsernames") && LLAvatarNameCache::useDisplayNames()) + if (gSavedSettings.getBOOL("NameTagShowUsernames") && + LLAvatarNameCache::useDisplayNames() && + !av_name.mIsDisplayNameDefault) { LLStyle::Params style_params_name; LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index c865dcf9a3..ba0dc31005 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -430,8 +430,9 @@ void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list& histo } else { - // Legacy chat logs only wrote the legacy name, not the agent_id - gCacheName->getUUID(from, from_id); + // convert it to a legacy name if we have a complete name + std::string legacy_name = gCacheName->buildLegacyName(from); + gCacheName->getUUID(legacy_name, from_id); } std::string timestamp = msg[IM_TIME]; @@ -526,8 +527,15 @@ bool LLIMModel::LLIMSession::isOtherParticipantAvaline() void LLIMModel::LLIMSession::onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name) { - // if username is empty, display names isn't enabled, use the display name - mHistoryFileName = av_name.mUsername.empty() ? av_name.mDisplayName : av_name.mUsername; + if (av_name.getLegacyName().empty()) + { + // if display names is off the legacy name will be the display name + mHistoryFileName = LLCacheName::cleanFullName(av_name.mDisplayName); + } + else + { + mHistoryFileName = LLCacheName::cleanFullName(av_name.getLegacyName()); + } } void LLIMModel::LLIMSession::buildHistoryFileName() @@ -737,8 +745,18 @@ bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")) - { - LLLogChat::saveHistory(file_name, from, from_id, utf8_text); + { + std::string from_name = from; + + LLAvatarName av_name; + if (!from_id.isNull() && + LLAvatarNameCache::get(from_id, &av_name) && + !av_name.mIsDisplayNameDefault) + { + from_name = av_name.getCompleteName(); + } + + LLLogChat::saveHistory(file_name, from_name, from_id, utf8_text); return true; } else diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index c8fd1e1d9a..8c70b1e973 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -26,18 +26,13 @@ #include "llviewerprecompiledheaders.h" -#include "lllogchat.h" - -// viewer includes #include "llagent.h" #include "llagentui.h" +#include "lllogchat.h" #include "lltrans.h" #include "llviewercontrol.h" -// library includes -#include "llchat.h" #include "llinstantmessage.h" -#include "llsdserialize.h" #include "llsingleton.h" // for LLSingleton #include @@ -65,7 +60,6 @@ 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 IM_SOURCE_TYPE("source_type"); const static std::string IM_SEPARATOR(": "); const static std::string NEW_LINE("\n"); @@ -93,7 +87,7 @@ const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+ * Regular expression suitable to match names like * "You", "Second Life", "Igor ProductEngine", "Object", "Mega House" */ -const static boost::regex NAME_AND_TEXT("(You:|Second Life:|[^\\s:]+\\s*[:]{1}|\\S+\\s+[^\\s:]+[:]{1})?(\\s*)(.*)"); +const static boost::regex NAME_AND_TEXT("([^:]+[:]{1})?(\\s*)(.*)"); //is used to parse complex object names like "Xstreet SL Terminal v2.2.5 st" const static std::string NAME_TEXT_DIVIDER(": "); @@ -190,8 +184,7 @@ std::string LLLogChat::makeLogFileName(std::string filename) { filename = cleanFileName(filename); filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename); - // new files are llsd notation format - filename += ".llsd"; + filename += ".txt"; return filename; } @@ -240,18 +233,6 @@ void LLLogChat::saveHistory(const std::string& filename, const std::string& from, const LLUUID& from_id, const std::string& line) -{ - LLChat chat; - chat.mText = line; - chat.mFromName = from; - chat.mFromID = from_id; - // default to being from an agent - chat.mSourceType = CHAT_SOURCE_AGENT; - saveHistory(filename, chat); -} - -//static -void LLLogChat::saveHistory(const std::string& filename, const LLChat& chat) { std::string tmp_filename = filename; LLStringUtil::trim(tmp_filename); @@ -273,27 +254,89 @@ void LLLogChat::saveHistory(const std::string& filename, const LLChat& chat) LLSD item; if (gSavedPerAccountSettings.getBOOL("LogTimestamp")) - item[IM_TIME] = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")); + item["time"] = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")); - item[IM_FROM_ID] = chat.mFromID; - item[IM_TEXT] = chat.mText; - item[IM_SOURCE_TYPE] = chat.mSourceType; + item["from_id"] = from_id; + item["message"] = line; //adding "Second Life:" for all system messages to make chat log history parsing more reliable - if (chat.mFromName.empty() && chat.mFromID.isNull()) + if (from.empty() && from_id.isNull()) { - item[IM_FROM] = SYSTEM_FROM; + item["from"] = SYSTEM_FROM; } else { - item[IM_FROM] = chat.mFromName; + item["from"] = from; } - file << LLSDOStreamer(item) << std::endl; + file << LLChatLogFormatter(item) << std::endl; file.close(); } +void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLineType, const LLSD&, void*), void* userdata) +{ + if(!filename.size()) + { + llwarns << "Filename is Empty!" << llendl; + return ; + } + + LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "r"); /*Flawfinder: ignore*/ + if (!fptr) + { + callback(LOG_EMPTY, LLSD(), userdata); + return; //No previous conversation with this name. + } + else + { + 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; + } + } + + 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) + { + 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); + } + } + else + { + firstline = FALSE; + } + } + callback(LOG_END, LLSD(), userdata); + + fclose(fptr); + } +} + void append_to_last_message(std::list& messages, const std::string& line) { if (!messages.size()) return; @@ -367,24 +410,52 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list& me fclose(fptr); } -// static -bool LLChatLogParser::parse(const std::string& raw, LLSD& im) +//*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 " +void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const { - if (!raw.length()) return false; + if (!im.isMap()) + { + llwarning("invalid LLSD type of an instant message", 0); + return; + } + + if (im[IM_TIME].isDefined()) +{ + std::string timestamp = im[IM_TIME].asString(); + boost::trim(timestamp); + ostr << '[' << timestamp << ']' << TWO_SPACES; + } - im = LLSD::emptyMap(); + //*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 " + if (im[IM_FROM].isDefined()) + { + std::string from = im[IM_FROM].asString(); + boost::trim(from); + if (from.size()) + { + ostr << from << IM_SEPARATOR; + } + } - // In Viewer 2.1 we added UUID to chat/IM logging so we can look up - // display names - if (raw[0] == '{') + if (im[IM_TEXT].isDefined()) { - // ...this is a viewer 2.1, new-style LLSD notation format log - std::istringstream raw_stream(raw); - LLPointer parser = new LLSDNotationParser(); - S32 count = parser->parse(raw_stream, im, raw.length()); - // expect several map items per parsed line - return (count != LLSDParser::PARSE_FAILURE); + std::string im_text = im[IM_TEXT].asString(); + + //multilined text will be saved with prepended spaces + boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX); + ostr << im_text; } + } + +bool LLChatLogParser::parse(std::string& raw, LLSD& im) +{ + if (!raw.length()) return false; + + im = LLSD::emptyMap(); //matching a timestamp boost::match_results matches; diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 8b1cc3484f..6958d56311 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -41,21 +41,48 @@ public: }; static std::string timestamp(bool withdate = false); static std::string makeLogFileName(std::string(filename)); - - // Log a single line item to the appropriate chat file - static void saveHistory(const std::string& filename, const LLChat& chat); - - // Prefer the above version - it saves more metadata about the item static void saveHistory(const std::string& filename, const std::string& from, const LLUUID& from_id, const std::string& line); + /** @deprecated @see loadAllHistory() */ + static void loadHistory(const std::string& filename, + void (*callback)(ELogLineType, const LLSD&, void*), + void* userdata); + static void loadAllHistory(const std::string& file_name, std::list& messages); private: static std::string cleanFileName(std::string filename); }; +/** + * Formatter for the plain text chat log files + */ +class LLChatLogFormatter +{ +public: + LLChatLogFormatter(const LLSD& im) : mIM(im) {} + virtual ~LLChatLogFormatter() {}; + + friend std::ostream& operator<<(std::ostream& str, const LLChatLogFormatter& formatter) + { + formatter.format(formatter.mIM, str); + return str; + } + +protected: + + /** + * Format an instant message to a stream + * Timestamps and sender names are required + * New lines of multilined messages are prepended with a space + */ + void format(const LLSD& im, std::ostream& ostr) const; + + LLSD mIM; +}; + /** * Parser for the plain text chat log files */ @@ -74,7 +101,7 @@ public: * * @return false if failed to parse mandatory data - message text */ - static bool parse(const std::string& raw, LLSD& im); + static bool parse(std::string& raw, LLSD& im); protected: LLChatLogParser(); @@ -86,6 +113,5 @@ extern const std::string IM_TIME; //("time"); extern const std::string IM_TEXT; //("message"); extern const std::string IM_FROM; //("from"); extern const std::string IM_FROM_ID; //("from_id"); -extern const std::string IM_SOURCE_TYPE; //("source_type"); #endif diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index f16cc4cef4..180695e40b 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -46,6 +46,8 @@ #include "llchathistory.h" #include "llstylemap.h" +#include "llavatarnamecache.h" + #include "lldraghandle.h" #include "llbottomtray.h" @@ -179,7 +181,21 @@ void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) if (gSavedPerAccountSettings.getBOOL("LogNearbyChat")) { - LLLogChat::saveHistory("chat", chat); + std::string from_name = chat.mFromName; + + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // if the chat is coming from an agent, log the complete name + LLAvatarName av_name; + LLAvatarNameCache::get(chat.mFromID, &av_name); + + if (!av_name.mIsDisplayNameDefault) + { + from_name = av_name.getCompleteName(); + } + } + + LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); } } @@ -248,11 +264,23 @@ void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue) nearby_chat->updateChatHistoryStyle(); } -bool isTwoWordsName(const std::string& name) +bool isWordsName(const std::string& name) { - //checking for a single space - S32 pos = name.find(' ', 0); - return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + // checking to see if it's display name plus username in parentheses + S32 open_paren = name.find(" (", 0); + S32 close_paren = name.find(')', 0); + + if (open_paren != std::string::npos && + close_paren == name.length()-1) + { + return true; + } + else + { + //checking for a single space + S32 pos = name.find(' ', 0); + return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + } } void LLNearbyChat::loadHistory() @@ -275,9 +303,10 @@ void LLNearbyChat::loadHistory() from_id = msg[IM_FROM_ID].asUUID(); } else - { - gCacheName->getUUID(from, from_id); - } + { + std::string legacy_name = gCacheName->buildLegacyName(from); + gCacheName->getUUID(legacy_name, from_id); + } LLChat chat; chat.mFromName = from; @@ -286,18 +315,15 @@ void LLNearbyChat::loadHistory() chat.mTimeStr = msg[IM_TIME].asString(); chat.mChatStyle = CHAT_STYLE_HISTORY; - if (msg.has(IM_SOURCE_TYPE)) - { - S32 source_type = msg[IM_SOURCE_TYPE].asInteger(); - chat.mSourceType = (EChatSourceType)source_type; - } - else if (from_id.isNull() && SYSTEM_FROM == from) + chat.mSourceType = CHAT_SOURCE_AGENT; + if (from_id.isNull() && SYSTEM_FROM == from) { chat.mSourceType = CHAT_SOURCE_SYSTEM; + } else if (from_id.isNull()) { - chat.mSourceType = isTwoWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; + chat.mSourceType = isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; } addMessage(chat, true, do_not_log); -- cgit v1.3 From 7c89e565b373803b64cabaac3695ac3e93469962 Mon Sep 17 00:00:00 2001 From: Wolfpup Lowenhar Date: Tue, 26 Oct 2010 20:02:07 -0400 Subject: This is the setting of the core file name date stamp code and settings files for Chat, Group and IM Logs. --HG-- branch : storm-102 --- doc/contributions.txt | 7 +++ .../newview/app_settings/settings_per_account.xml | 12 ++++- indra/newview/llfloaterpreference.cpp | 2 +- indra/newview/lllogchat.cpp | 62 ++++++++++++++++++++-- indra/newview/lllogchat.h | 5 ++ .../default/xui/en/panel_preferences_privacy.xml | 12 ++++- 6 files changed, 94 insertions(+), 6 deletions(-) (limited to 'indra/newview/lllogchat.cpp') diff --git a/doc/contributions.txt b/doc/contributions.txt index 96ee446a0c..4c199672cc 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -749,6 +749,13 @@ Whoops Babii VWR-8298 Wilton Lundquist VWR-7682 +WolfPup Lowenhar + SNOW-622 + SNOW-772 + STORM-102 + STORM-103 + VWR-20741 + VWR-20933 Zai Lynch VWR-19505 Zarkonnen Decosta diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index dc76a4e518..ab702e49e1 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -110,7 +110,17 @@ Value 00000000-0000-0000-0000-000000000000 - + LogFileNamewithDate + + Comment + Add Date Stamp to chat and IM Logs with format chat-YYYY-MM-DD and 'IM file name'-YYYY-MM. To view old logs goto ..\Second Life\[login name] + Persist + 1 + Type + Boolean + Value + 1 +