dino/libdino/src/entity/message.vala

254 lines
9.0 KiB
Vala

using Gee;
using Xmpp;
namespace Dino.Entities {
public class Message : Object {
public const bool DIRECTION_SENT = true;
public const bool DIRECTION_RECEIVED = false;
public enum Marked {
NONE,
RECEIVED,
READ,
ACKNOWLEDGED,
UNSENT,
WONTSEND,
SENDING,
SENT,
ERROR
}
public static Marked[] MARKED_RECEIVED = new Marked[] { Marked.READ, Marked.RECEIVED, Marked.ACKNOWLEDGED };
public enum Type {
ERROR,
CHAT,
GROUPCHAT,
GROUPCHAT_PM,
UNKNOWN;
public bool is_muc_semantic() {
return this == GROUPCHAT || this == GROUPCHAT_PM;
}
}
public int id { get; set; default = -1; }
public Account account { get; set; }
public Jid? counterpart { get; set; }
public Jid? ourpart { get; set; }
public Jid? from {
get { return direction == DIRECTION_SENT ? ourpart : counterpart; }
}
public Jid? to {
get { return direction == DIRECTION_SENT ? counterpart : ourpart; }
}
public bool direction { get; set; }
public Jid? real_jid { get; set; }
public Type type_ { get; set; default = Type.UNKNOWN; }
private string? body_;
public string? body {
get { return body_; }
set { body_ = value != null ? value.make_valid() : null; }
}
public string? stanza_id { get; set; }
public string? server_id { get; set; }
public DateTime? time { get; set; }
/** UTC **/
public DateTime? local_time { get; set; }
public Encryption encryption { get; set; default = Encryption.NONE; }
private Marked marked_ = Marked.NONE;
public Marked marked {
get { return marked_; }
set {
if (value == Marked.RECEIVED && marked == Marked.READ) return;
marked_ = value;
}
}
public string? edit_to = null;
public int quoted_item_id = 0;
private Gee.List<Xep.FallbackIndication.Fallback> fallbacks = null;
private Database? db;
public Message(string? body) {
this.body = body;
}
public Message.from_row(Database db, Qlite.Row row) throws InvalidJidError {
this.db = db;
id = row[db.message.id];
account = db.get_account_by_id(row[db.message.account_id]);
stanza_id = row[db.message.stanza_id];
server_id = row[db.message.server_id];
type_ = (Message.Type) row[db.message.type_];
counterpart = db.get_jid_by_id(row[db.message.counterpart_id]);
string counterpart_resource = row[db.message.counterpart_resource];
if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource);
string our_resource = row[db.message.our_resource];
if (type_ == Type.GROUPCHAT && our_resource != null) {
ourpart = counterpart.with_resource(our_resource);
} else if (our_resource != null) {
ourpart = account.bare_jid.with_resource(our_resource);
} else {
ourpart = account.bare_jid;
}
direction = row[db.message.direction];
time = new DateTime.from_unix_utc(row[db.message.time]);
local_time = new DateTime.from_unix_utc(row[db.message.local_time]);
body = row[db.message.body];
marked = (Message.Marked) row[db.message.marked];
encryption = (Encryption) row[db.message.encryption];
string? real_jid_str = row[db.real_jid.real_jid];
if (real_jid_str != null) real_jid = new Jid(real_jid_str);
edit_to = row[db.message_correction.to_stanza_id];
quoted_item_id = row[db.reply.quoted_content_item_id];
notify.connect(on_update);
}
public void persist(Database db) {
if (id != -1) return;
this.db = db;
Qlite.InsertBuilder builder = db.message.insert()
.value(db.message.account_id, account.id)
.value(db.message.counterpart_id, db.get_jid_id(counterpart))
.value(db.message.counterpart_resource, counterpart.resourcepart)
.value(db.message.our_resource, ourpart.resourcepart)
.value(db.message.direction, direction)
.value(db.message.type_, type_)
.value(db.message.time, (long) time.to_unix())
.value(db.message.local_time, (long) local_time.to_unix())
.value(db.message.body, body)
.value(db.message.encryption, encryption)
.value(db.message.marked, marked);
if (stanza_id != null) builder.value(db.message.stanza_id, stanza_id);
if (server_id != null) builder.value(db.message.server_id, server_id);
id = (int) builder.perform();
if (real_jid != null) {
db.real_jid.insert()
.value(db.real_jid.message_id, id)
.value(db.real_jid.real_jid, real_jid.to_string())
.perform();
}
notify.connect(on_update);
}
public Gee.List<Xep.FallbackIndication.Fallback> get_fallbacks() {
if (fallbacks != null) return fallbacks;
var fallbacks_by_ns = new HashMap<string, ArrayList<Xep.FallbackIndication.FallbackLocation>>();
foreach (Qlite.Row row in db.body_meta.select().with(db.body_meta.message_id, "=", id)) {
if (row[db.body_meta.info_type] != Xep.FallbackIndication.NS_URI) continue;
string ns_uri = row[db.body_meta.info];
if (!fallbacks_by_ns.has_key(ns_uri)) {
fallbacks_by_ns[ns_uri] = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
}
fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation(row[db.body_meta.from_char], row[db.body_meta.to_char]));
}
var fallbacks = new ArrayList<Xep.FallbackIndication.Fallback>();
foreach (string ns_uri in fallbacks_by_ns.keys) {
fallbacks.add(new Xep.FallbackIndication.Fallback(ns_uri, fallbacks_by_ns[ns_uri].to_array()));
}
this.fallbacks = fallbacks;
return fallbacks;
}
public void set_fallbacks(Gee.List<Xep.FallbackIndication.Fallback> fallbacks) {
this.fallbacks = fallbacks;
}
public void set_type_string(string type) {
switch (type) {
case Xmpp.MessageStanza.TYPE_CHAT:
type_ = Type.CHAT; break;
case Xmpp.MessageStanza.TYPE_GROUPCHAT:
type_ = Type.GROUPCHAT; break;
}
}
public new string get_type_string() {
switch (type_) {
case Type.CHAT:
return Xmpp.MessageStanza.TYPE_CHAT;
case Type.GROUPCHAT:
return Xmpp.MessageStanza.TYPE_GROUPCHAT;
default:
return Xmpp.MessageStanza.TYPE_NORMAL;
}
}
public bool equals(Message? m) {
if (m == null) return false;
return equals_func(this, m);
}
public static bool equals_func(Message m1, Message m2) {
if (m1.stanza_id == m2.stanza_id &&
m1.body == m2.body) {
return true;
}
return false;
}
public static uint hash_func(Message message) {
return message.body.hash();
}
private void on_update(Object o, ParamSpec sp) {
Qlite.UpdateBuilder update_builder = db.message.update().with(db.message.id, "=", id);
switch (sp.name) {
case "stanza-id":
update_builder.set(db.message.stanza_id, stanza_id); break;
case "server-id":
update_builder.set(db.message.server_id, server_id); break;
case "counterpart":
update_builder.set(db.message.counterpart_id, db.get_jid_id(counterpart));
update_builder.set(db.message.counterpart_resource, counterpart.resourcepart); break;
case "ourpart":
update_builder.set(db.message.our_resource, ourpart.resourcepart); break;
case "direction":
update_builder.set(db.message.direction, direction); break;
case "type-":
update_builder.set(db.message.type_, type_); break;
case "time":
update_builder.set(db.message.time, (long) time.to_unix()); break;
case "local-time":
update_builder.set(db.message.local_time, (long) local_time.to_unix()); break;
case "body":
update_builder.set(db.message.body, body); break;
case "encryption":
update_builder.set(db.message.encryption, encryption); break;
case "marked":
update_builder.set(db.message.marked, marked); break;
}
update_builder.perform();
if (sp.get_name() == "real-jid") {
db.real_jid.upsert()
.value(db.real_jid.message_id, id, true)
.value(db.real_jid.real_jid, real_jid.to_string())
.perform();
}
if (sp.get_name() == "quoted-item-id") {
db.reply.upsert()
.value(db.reply.message_id, id, true)
.value(db.reply.quoted_content_item_id, quoted_item_id)
.perform();
}
}
}
}