diff options
| author | Leyla Farazha <leyla@lindenlab.com> | 2010-10-22 17:41:06 -0700 | 
|---|---|---|
| committer | Leyla Farazha <leyla@lindenlab.com> | 2010-10-22 17:41:06 -0700 | 
| commit | cb5d8d1a9295076327f23e5f6d6c91fd0d4580ea (patch) | |
| tree | 7e57893b40aa5d63aad08d28b0f62843ad4ed31c /indra/newview | |
| parent | 48e6f36f987623ca5040de1365f9454eb074d131 (diff) | |
DN-181 Chat & IM logs saved in unreadable .llsd instead of .txt
Diffstat (limited to 'indra/newview')
| -rw-r--r-- | indra/newview/llchathistory.cpp | 47 | ||||
| -rw-r--r-- | indra/newview/llimview.cpp | 30 | ||||
| -rw-r--r-- | indra/newview/lllogchat.cpp | 157 | ||||
| -rw-r--r-- | indra/newview/lllogchat.h | 40 | ||||
| -rw-r--r-- | indra/newview/llnearbychat.cpp | 56 | 
5 files changed, 254 insertions, 76 deletions
| 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<LLTextBox>("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<LLSD>& 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 <boost/algorithm/string/trim.hpp> @@ -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;  } @@ -241,18 +234,6 @@ void LLLogChat::saveHistory(const std::string& filename,  			    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);  	if (tmp_filename.empty()) @@ -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<LLSDNotationFormatter>(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<LLSD>& messages, const std::string& line)  {  	if (!messages.size()) return; @@ -367,24 +410,52 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& 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 <actual_object's_name>" +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 <actual_object's_name>" +	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<LLSDParser> 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<std::string::const_iterator> 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,22 +41,49 @@ 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<LLSD>& 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   */  class LLChatLogParser @@ -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); | 
