Store chatlogs in binary and text formats
If chat logs are stored in an endless plain-text file, they're going to be a pain to parse. If they're stored in a binary file, they're a pain for the user to `grep` or go through manually, but they're easier to parse. Why not both? Logs are now stored with a BMessage (with BStringLists) in the 'logs' attribute, and the plain-text logs in the file itself. The attribute will only store 20 messages, but the file itself will be appended to forever. The logs directory also changed, from ~/config/settings/Caya/Cache/Logs to ~/config/settings/Caya/Logs. Useful functions for reading/writing messages to and from an attribute were borrowed from BePodder's libfunky. :-)
This commit is contained in:
parent
fe99f3d83a
commit
dc3fdd65c8
|
@ -1,11 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
|
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
|
||||||
|
* Copyright 2014, Funky Idea Software
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
|
||||||
* Authors:
|
|
||||||
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
|
||||||
#include <Bitmap.h>
|
#include <Bitmap.h>
|
||||||
|
@ -15,6 +12,8 @@
|
||||||
#include <IconUtils.h>
|
#include <IconUtils.h>
|
||||||
#include <Path.h>
|
#include <Path.h>
|
||||||
|
|
||||||
|
#include <kernel/fs_attr.h>
|
||||||
|
|
||||||
#include "CayaUtils.h"
|
#include "CayaUtils.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,8 +128,11 @@ CayaCachePath()
|
||||||
const char*
|
const char*
|
||||||
CayaLogPath(const char* signature, const char* subsignature)
|
CayaLogPath(const char* signature, const char* subsignature)
|
||||||
{
|
{
|
||||||
BPath path(CayaCachePath());
|
BPath path;
|
||||||
path.Append("Logs");
|
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
path.Append("Caya/Logs");
|
||||||
path.Append(signature);
|
path.Append(signature);
|
||||||
|
|
||||||
if (BString(signature) != BString(subsignature)
|
if (BString(signature) != BString(subsignature)
|
||||||
|
@ -175,6 +177,62 @@ CayaTintColor(rgb_color color, int severity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ReadAttributeData(BNode* node, const char* name, char** buffer, int32 *size) {
|
||||||
|
attr_info info;
|
||||||
|
status_t ret = node->GetAttrInfo(name, &info);
|
||||||
|
|
||||||
|
if (ret == B_OK) {
|
||||||
|
*buffer = (char *)calloc(info.size, sizeof(char));
|
||||||
|
ret = node->ReadAttr(name, info.type, 0, *buffer, info.size);
|
||||||
|
|
||||||
|
if (ret > B_OK) {
|
||||||
|
*size = ret;
|
||||||
|
ret = B_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
free(*buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
WriteAttributeMessage(BNode* node, const char* name, BMessage* data)
|
||||||
|
{
|
||||||
|
BMallocIO malloc;
|
||||||
|
status_t ret=data->Flatten(&malloc);
|
||||||
|
|
||||||
|
if( ret == B_OK) {
|
||||||
|
ret = node->WriteAttr(name,B_ANY_TYPE,0,malloc.Buffer(),malloc.BufferLength());
|
||||||
|
|
||||||
|
if(ret > B_OK)
|
||||||
|
ret=B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ReadAttributeMessage(BNode* node, const char* name, BMessage* data)
|
||||||
|
{
|
||||||
|
char *buffer = NULL;
|
||||||
|
int32 size = 0;
|
||||||
|
|
||||||
|
status_t ret = ReadAttributeData(node,name,&buffer,&size);
|
||||||
|
|
||||||
|
if(size>0 && buffer!=NULL) {
|
||||||
|
BMemoryIO mem(buffer,size);
|
||||||
|
ret = data->Unflatten(&mem);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
|
|
|
@ -24,9 +24,13 @@ const char* CayaAccountPath(const char* signature, const char* subsignature);
|
||||||
const char* CayaCachePath();
|
const char* CayaCachePath();
|
||||||
const char* CayaLogPath(const char* signature, const char* subsignature);
|
const char* CayaLogPath(const char* signature, const char* subsignature);
|
||||||
|
|
||||||
// Will return a tinted color― light or dark― based on brightness.
|
|
||||||
rgb_color CayaTintColor(rgb_color color, int severity);
|
rgb_color CayaTintColor(rgb_color color, int severity);
|
||||||
|
|
||||||
|
// Borrowed from BePodder's own libfunky
|
||||||
|
status_t ReadAttributeData(BNode* node, const char* name, char** buffer, int32 *size);
|
||||||
|
status_t WriteAttributeMessage(BNode* node, const char* name, BMessage* data);
|
||||||
|
status_t ReadAttributeMessage(BNode* node, const char* name, BMessage* data);
|
||||||
|
|
||||||
extern "C" status_t our_image(image_info& image);
|
extern "C" status_t our_image(image_info& image);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -173,10 +173,8 @@ Conversation::GetView()
|
||||||
if (fLooper->Protocol()->SaveLogs() == false)
|
if (fLooper->Protocol()->SaveLogs() == false)
|
||||||
return fChatView;
|
return fChatView;
|
||||||
|
|
||||||
BStringList logs = _GetChatLogs();
|
BMessage logMsg;
|
||||||
BMessage logMsg(IM_MESSAGE);
|
if (_GetChatLogs(&logMsg) == B_OK)
|
||||||
logMsg.AddInt32("im_what", IM_LOGS_RECEIVED);
|
|
||||||
logMsg.AddStrings("body", logs);
|
|
||||||
fChatView->MessageReceived(&logMsg);
|
fChatView->MessageReceived(&logMsg);
|
||||||
|
|
||||||
RegisterObserver(fChatView);
|
RegisterObserver(fChatView);
|
||||||
|
@ -266,8 +264,32 @@ Conversation::_LogChatMessage(BMessage* msg)
|
||||||
fDateFormatter.Format(date, time(0), B_SHORT_DATE_FORMAT, B_MEDIUM_TIME_FORMAT);
|
fDateFormatter.Format(date, time(0), B_SHORT_DATE_FORMAT, B_MEDIUM_TIME_FORMAT);
|
||||||
|
|
||||||
BString id = msg->FindString("user_id");
|
BString id = msg->FindString("user_id");
|
||||||
BString uname;
|
BString body = msg->FindString("body");
|
||||||
|
|
||||||
|
// Binary logs
|
||||||
|
// TODO: Don't hardcode 21, expose maximum as a setting
|
||||||
|
BStringList users, bodies;
|
||||||
|
|
||||||
|
BMessage logMsg;
|
||||||
|
if (_GetChatLogs(&logMsg) == B_OK) {
|
||||||
|
logMsg.FindStrings("body", &bodies);
|
||||||
|
logMsg.FindStrings("user_id", &users);
|
||||||
|
bodies.Remove(21);
|
||||||
|
users.Remove(21);
|
||||||
|
bodies.Add(body, 0);
|
||||||
|
users.Add(id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BMessage newLogMsg(IM_MESSAGE);
|
||||||
|
newLogMsg.AddInt32("im_what", IM_LOGS_RECEIVED);
|
||||||
|
newLogMsg.AddStrings("body", bodies);
|
||||||
|
newLogMsg.AddStrings("user_id", users);
|
||||||
|
|
||||||
|
BFile logFile(fLogPath.Path(), B_READ_WRITE | B_OPEN_AT_END | B_CREATE_FILE);
|
||||||
|
WriteAttributeMessage(&logFile, "logs", &newLogMsg);
|
||||||
|
|
||||||
|
// Plain-text logs
|
||||||
|
BString uname;
|
||||||
if (id.IsEmpty() == false)
|
if (id.IsEmpty() == false)
|
||||||
uname = UserById(id)->GetName();
|
uname = UserById(id)->GetName();
|
||||||
else
|
else
|
||||||
|
@ -278,37 +300,21 @@ Conversation::_LogChatMessage(BMessage* msg)
|
||||||
logLine << "] ";
|
logLine << "] ";
|
||||||
logLine << uname;
|
logLine << uname;
|
||||||
logLine << ": ";
|
logLine << ": ";
|
||||||
logLine << msg->FindString("body");
|
logLine << body;
|
||||||
logLine << "\n";
|
logLine << "\n";
|
||||||
|
|
||||||
|
logFile.Write(logLine.String(), logLine.Length());
|
||||||
// TODO: Don't hardcode 21, expose maximum as a setting
|
|
||||||
BStringList logs = _GetChatLogs();
|
|
||||||
logs.Remove(21);
|
|
||||||
logs.Add(logLine, 0);
|
|
||||||
|
|
||||||
BMessage newLogMsg;
|
|
||||||
newLogMsg.AddStrings("log", logs);
|
|
||||||
|
|
||||||
BFile logFile(fLogPath.Path(), B_READ_WRITE | B_CREATE_FILE);
|
|
||||||
newLogMsg.Flatten(&logFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BStringList
|
status_t
|
||||||
Conversation::_GetChatLogs()
|
Conversation::_GetChatLogs(BMessage* msg)
|
||||||
{
|
{
|
||||||
_EnsureLogPath();
|
_EnsureLogPath();
|
||||||
|
|
||||||
BFile logFile(fLogPath.Path(), B_READ_WRITE | B_CREATE_FILE);
|
BFile logFile(fLogPath.Path(), B_READ_WRITE | B_CREATE_FILE);
|
||||||
BMessage logMsg;
|
|
||||||
BStringList logs;
|
|
||||||
|
|
||||||
if (logMsg.Unflatten(&logFile) == B_OK) {
|
return ReadAttributeMessage(&logFile, "logs", msg);
|
||||||
logMsg.FindStrings("log", &logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return logs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _LogChatMessage(BMessage* msg);
|
void _LogChatMessage(BMessage* msg);
|
||||||
BStringList _GetChatLogs();
|
status_t _GetChatLogs(BMessage* msg);
|
||||||
void _EnsureLogPath();
|
void _EnsureLogPath();
|
||||||
|
|
||||||
User* _EnsureUser(BMessage* msg);
|
User* _EnsureUser(BMessage* msg);
|
||||||
|
|
|
@ -275,31 +275,25 @@ ConversationView::_AppendOrEnqueueMessage(BMessage* msg)
|
||||||
void
|
void
|
||||||
ConversationView::_AppendMessage(BMessage* msg)
|
ConversationView::_AppendMessage(BMessage* msg)
|
||||||
{
|
{
|
||||||
|
BStringList users, bodies;
|
||||||
BString message = msg->FindString("body");
|
if (msg->FindStrings("body", &bodies) != B_OK
|
||||||
BString id = msg->FindString("user_id");
|
|| msg->FindStrings("user_id", &users) != B_OK)
|
||||||
BString uname = "";
|
|
||||||
|
|
||||||
if (id.IsEmpty() == false) {
|
|
||||||
User* sender = fConversation->UserById(id);
|
|
||||||
|
|
||||||
if (sender == NULL || (uname = sender->GetName()) == NULL)
|
|
||||||
uname = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg->FindInt32("im_what") == IM_MESSAGE_SENT)
|
|
||||||
fReceiveView->AppendOwnMessage(message.String());
|
|
||||||
|
|
||||||
else if (uname.IsEmpty() == false)
|
|
||||||
fReceiveView->AppendOtherMessage(uname.String(), message.String());
|
|
||||||
|
|
||||||
else {
|
|
||||||
BStringList bodies;
|
|
||||||
if (msg->FindStrings("body", &bodies) != B_OK)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = bodies.CountStrings(); i >= 0; i--)
|
for (int i = bodies.CountStrings(); i >= 0; i--) {
|
||||||
fReceiveView->AppendGenericMessage(bodies.StringAt(i).String());
|
User* sender = fConversation->UserById(users.StringAt(i));
|
||||||
|
BString sender_name;
|
||||||
|
BString body = bodies.StringAt(i);
|
||||||
|
|
||||||
|
if (sender != NULL)
|
||||||
|
sender_name = sender->GetName();
|
||||||
|
|
||||||
|
if (sender_name.IsEmpty() == true) {
|
||||||
|
fReceiveView->AppendGenericMessage(body.String());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fReceiveView->AppendOtherMessage(sender_name.String(), body.String());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -688,7 +688,7 @@ JabberHandler::_MessageSent(const char* id, const char* subject,
|
||||||
{
|
{
|
||||||
BMessage msg(IM_MESSAGE);
|
BMessage msg(IM_MESSAGE);
|
||||||
msg.AddInt32("im_what", IM_MESSAGE_SENT);
|
msg.AddInt32("im_what", IM_MESSAGE_SENT);
|
||||||
msg.AddString("user_id", id);
|
msg.AddString("user_id", fJid.bare().c_str());
|
||||||
msg.AddString("chat_id", id);
|
msg.AddString("chat_id", id);
|
||||||
msg.AddString("subject", subject);
|
msg.AddString("subject", subject);
|
||||||
msg.AddString("body", body);
|
msg.AddString("body", body);
|
||||||
|
|
Ŝarĝante…
Reference in New Issue