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 Richardmeow-7.2.2
parent
a282ffedf4
commit
b34eee98d1
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <boost/algorithm/string/trim.hpp>
|
||||
|
|
@ -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<LLSDNotationFormatter>(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;
|
||||
|
|
@ -352,6 +309,7 @@ void append_to_last_message(std::list<LLSD>& messages, const std::string& line)
|
|||
messages.back()[IM_TEXT] = im_text;
|
||||
}
|
||||
|
||||
// static
|
||||
void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& messages)
|
||||
{
|
||||
if (file_name.empty())
|
||||
|
|
@ -415,53 +373,7 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& 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 <actual_object's_name>"
|
||||
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 <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;
|
||||
}
|
||||
}
|
||||
|
||||
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<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);
|
||||
}
|
||||
|
||||
//matching a timestamp
|
||||
boost::match_results<std::string::const_iterator> 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)
|
||||
|
|
|
|||
|
|
@ -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<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
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue