diff --git a/src/Store3Mail3/Mail3.h b/src/Store3Mail3/Mail3.h --- a/src/Store3Mail3/Mail3.h +++ b/src/Store3Mail3/Mail3.h @@ -1,807 +1,809 @@ #ifndef _MAIL3_H_ #define _MAIL3_H_ #include "lgi/common/Lgi.h" #include "Store3Common.h" #include "v3.41.2/sqlite3.h" #include "Store3CalendarObj.h" // Debugging stuff #define MAIL3_TRACK_OBJS 0 #define MAIL3_DB_FILE "Database.sqlite" #define MAIL3_TBL_FOLDER "Folder" #define MAIL3_TBL_FOLDER_FLDS "FolderFields" #define MAIL3_TBL_MAIL "Mail" #define MAIL3_TBL_MAILSEGS "MailSegs" #define MAIL3_TBL_CONTACT "Contact" #define MAIL3_TBL_GROUP "ContactGroup" #define MAIL3_TBL_FILTER "Filter" #define MAIL3_TBL_CALENDAR "Calendar" #define MAIL3_TBL_CALENDAR_FILES "CalendarFiles" class LMail3Store; class LMail3Mail; class LMail3Calendar; enum Mail3SubFormat { Mail3v1, // All the mail and segs in a single tables. Mail3v2, // All the mail and segs in per folders tables. }; struct LMail3Def { const char *Name; const char *Type; }; struct GMail3Idx { const char *IdxName; const char *Table; const char *Column; }; class Mail3BlobStream : public LStream { LMail3Store *Store; sqlite3_blob *b; int64 Pos, Size; int SegId; bool WriteAccess; bool OpenBlob(); bool CloseBlob(); public: const char *File; int Line; Mail3BlobStream(LMail3Store *Store, int segId, int size, const char *file, int line, bool Write = false); ~Mail3BlobStream(); int64 GetPos(); int64 SetPos(int64 p); int64 GetSize(); int64 SetSize(int64 sz); ssize_t Read(void *Buf, ssize_t Len, int Flags = 0); ssize_t Write(const void *Buf, ssize_t Len, int Flags = 0); }; extern LMail3Def TblFolder[]; extern LMail3Def TblFolderFlds[]; extern LMail3Def TblMail[]; extern LMail3Def TblMailSegs[]; extern LMail3Def TblContact[]; extern LMail3Def TblFilter[]; extern LMail3Def TblGroup[]; extern LMail3Def TblCalendar[]; extern LMail3Def TblCalendarFiles[]; #define SERIALIZE_STR(Var, Col) \ if (Write) \ { \ if (!s.SetStr(Col, Var.Str())) \ return false; \ } \ else \ Var = s.GetStr(Col); #define SERIALIZE_DATE(Var, Col) \ if (Write) \ { \ if (Var.IsValid()) Var.ToUtc(); \ if (!s.SetDate(Col, Var)) \ return false; \ } \ else \ { \ int Fmt = Var.GetFormat(); \ Var.SetFormat(GDTF_YEAR_MONTH_DAY|GDTF_24HOUR); \ Var.Set(s.GetStr(Col)); \ Var.SetFormat(Fmt); \ Var.SetTimeZone(0, false); \ } #define SERIALIZE_BOOL(Var, Col) \ if (Write) \ { \ if (!s.SetInt(Col, Var)) \ return false; \ } \ else \ Var = s.GetBool(Col); #define SERIALIZE_INT(Var, Col) \ if (Write) \ { \ if (!s.SetInt(Col, Var)) \ return false; \ } \ else \ Var = s.GetInt(Col); #define SERIALIZE_INT64(Var, Col) \ if (Write) \ { \ if (!s.SetInt64(Col, Var)) \ return false; \ } \ else \ Var = s.GetInt64(Col); #define SERIALIZE_COLOUR(Colour, Col) \ if (Write) \ { \ int64_t c = Colour.IsValid() ? Colour.c32() : 0; \ if (!s.SetInt64(Col, c)) \ return false; \ } \ else \ { \ int64_t c = s.GetInt64(Col); \ if (c > 0) Colour.c32((uint32_t)c); \ else Colour.Empty(); \ } #define SERIALIZE_AUTOSTR(var, Col) \ { \ if (Write) \ { \ if (!s.SetStr(Col, var)) \ return false; \ } \ else \ { \ var.Reset(NewStr(s.GetStr(Col))); \ } \ } #define SERIALIZE_LSTR(var, Col) \ { \ if (Write) \ { \ if (!s.SetStr(Col, var)) \ return false; \ } \ else \ { \ var = s.GetStr(Col); \ } \ } struct LMail3StoreMsg { enum Type { MsgNone, MsgCompactComplete, MsgRepairComplete, } Msg; int64_t Int; LString Str; LMail3StoreMsg(Type type) { Msg = type; } }; class LMail3Store : public LDataStoreI { friend class LMail3Obj; friend class LMail3Mail; friend class Mail3Trans; friend class CompactThread; LDataEventsI *Callback; sqlite3 *Db; LString Folder; LString DbFile; class LMail3Folder *Root; LHashTbl, LMail3Def*> Fields; LHashTbl, GMail3Idx*> Indexes; Store3Status OpenStatus; LHashTbl, Store3Status> TableStatus; LString ErrorMsg; LString StatusMsg; LString TempPath; std::function CompactOnStatus; std::function RepairOnStatus; struct TableDefn : LArray { LString::Array t; }; bool ParseTableFormat(const char *Name, TableDefn &Defs); Store3Status CheckTable(const char *Name, LMail3Def *Flds); bool UpdateTable(const char *Name, LMail3Def *Flds, Store3Status Check); bool DeleteMailById(int64 Id); bool OpenDb(); bool CloseDb(); LMail3Folder *GetSystemFolder(int Type); public: Mail3SubFormat Format; class LStatement { struct PostBlob { int64 Size; LVariant ColName; LStreamI *Data; }; LArray Post; protected: LMail3Store *Store; sqlite3_stmt *s; LVariant Table; LAutoString TempSql; public: LStatement(LMail3Store *store, const char *sql = NULL); virtual ~LStatement(); operator sqlite3_stmt *() { return s; } bool IsOk() { return #ifndef __llvm__ this != 0 && #endif Store != 0 && s != 0; } bool Prepare(const char *Sql); bool Row(); bool Exec(); bool Reset(); bool Finalize(); int64 LastInsertId(); int GetSize(int Col); bool GetBool(int Col); int GetInt(int Col); bool SetInt(int Col, int n); int64 GetInt64(int Col); bool SetInt64(int Col, int64 n); char *GetStr(int Col); bool GetBinary(int Col, LVariant *v); bool SetStr(int Col, const char *s); bool SetDate(int Col, LDateTime &d); bool SetStream(int Col, const char *ColName, LStreamI *s); bool SetBinary(int Col, const char *ColName, LVariant *v); virtual int64 GetRowId() { LAssert(0); return -1; } }; class LInsert : public LStatement { public: LInsert(LMail3Store *store, const char *Tbl); int64 GetRowId() { return LastInsertId(); } }; class LUpdate : public LStatement { int64 RowId; public: LUpdate(LMail3Store *store, const char *Tbl, int64 rowId, char *ExcludeField = NULL); int64 GetRowId() { return RowId; } }; class LTransaction { LMail3Store *Store; bool Open; public: LTransaction(LMail3Store *store); ~LTransaction(); bool RollBack(); }; #if MAIL3_TRACK_OBJS struct SqliteObjs { LStatement *Stat; Mail3BlobStream *Stream; SqliteObjs() { Stat = 0; Stream = 0; } }; LArray All; template bool RemoveFromAll(T *p) { for (int i=0; i &Items) override; Store3Status Delete(LArray &Items, bool ToTrash) override; Store3Status Change(LArray &Items, int PropId, LVariant &Value, LOperator Operator) override; void Compact(LViewI *Parent, LDataPropI *Props, std::function OnStatus) override; void Upgrade(LViewI *Parent, LDataPropI *Props, std::function OnStatus) override; void Repair(LViewI *Parent, LDataPropI *Props, std::function OnStatus) override; bool SetFormat(LViewI *Parent, LDataPropI *Props) override; void PostStore(LMail3StoreMsg *m) { Callback->Post(this, m); } void OnEvent(void *Param) override; bool Check(int Code, const char *Sql); LMail3Def *GetFields(const char *t) { return Fields.Find(t); } StoreTrans StartTransaction() override; // LDataEventsI wrappers void OnNew(const char *File, int Line, LDataFolderI *parent, LArray &new_items, int pos, bool is_new); bool OnMove(const char *File, int Line, LDataFolderI *new_parent, LDataFolderI *old_parent, LArray &items); bool OnChange(const char *File, int Line, LArray &items, int FieldHint); bool OnDelete(const char *File, int Line, LDataFolderI *parent, LArray &items); private: class Mail3Trans : public LDataStoreI::LDsTransaction { Mail3Trans **Ptr; LTransaction Trans; public: Mail3Trans(LMail3Store *s, Mail3Trans **ptr); ~Mail3Trans(); } *Transaction; }; class LMail3Obj { protected: bool Check(int r, char *sql); public: LMail3Store *Store; int64 Id; int64 ParentId; LMail3Obj(LMail3Store *store) { Id = ParentId = -1; Store = store; } bool Write(const char *Table, bool Insert); virtual bool SetId(int64_t id) { Id = id; return Id >= 0; } virtual const char *GetClass() { return "LMail3Obj"; } virtual bool Serialize(LMail3Store::LStatement &s, bool Write) = 0; virtual void SetStore(LMail3Store *s) { Store = s; } }; class LMail3Thing : public LDataI, public LMail3Obj { friend class LMail3Store; LMail3Thing &operator =(LMail3Thing &p) = delete; protected: bool NewMail = false; virtual const char *GetTable() { LAssert(0); return 0; } virtual void OnSave() {}; public: LMail3Folder *Parent = NULL; LMail3Thing(LMail3Store *store = NULL) : LMail3Obj(store) { } ~LMail3Thing(); const char *GetClass() { return "LMail3Thing"; } bool IsOnDisk() { return Id > 0; } bool IsOrphan() { return Store == NULL || Parent == NULL; } uint64 Size() { return sizeof(*this); } uint32_t Type() { LAssert(0); return 0; } Store3Status Delete(bool ToTrash); LDataStoreI *GetStore() { return Store; } LAutoStreamI GetStream(const char *file, int line) { LAssert(0); return LAutoStreamI(0); } bool Serialize(LMail3Store::LStatement &s, bool Write) { LAssert(0); return false; } Store3Status Save(LDataI *Folder = NULL); virtual bool DbDelete() { LAssert(0); return false; } }; class LMail3Folder : public LDataFolderI, public LMail3Obj { public: LVariant Name; int Unread; int Open; int ItemType; int Sort; // Which field to sort contents on int Threaded; int SiblingIndex; // The index of this folder when sorting amongst other sibling folders union { int AccessPerms; struct { int16_t ReadPerm; int16_t WritePerm; }; }; Store3SystemFolder System; LMail3Folder *Parent; DIterator Sub; DIterator Items; DIterator Flds; LMail3Folder(LMail3Store *store); ~LMail3Folder(); Store3CopyDecl; bool Serialize(LMail3Store::LStatement &s, bool Write) override; const char *GetClass() override { return "LMail3Folder"; } uint32_t Type() override; bool IsOnDisk() override; bool IsOrphan() override; uint64 Size() override; Store3Status Save(LDataI *Folder) override; Store3Status Delete(bool ToTrash) override; LDataStoreI *GetStore() override; LAutoStreamI GetStream(const char *file, int line) override; bool SetStream(LAutoStreamI stream) override; LDataIterator &SubFolders() override; LDataIterator &Children() override; LDataIterator &Fields() override; Store3Status DeleteAllChildren() override; Store3Status FreeChildren() override; LMail3Folder *FindSub(char *Name); bool DbDelete(); bool GenSizes(); const char *GetStr(int id) override; Store3Status SetStr(int id, const char *str) override; int64 GetInt(int id) override; Store3Status SetInt(int id, int64 i) override; }; class LMail3Attachment : public Store3Attachment { int64 SegId; int64 BlobSize; LString Headers; LString Name; LString MimeType; LString ContentId; LString Charset; /// This is set when the segment is not to be stored on disk. /// When signed and/or encrypted messages are stored, the original /// rfc822 image is maintained by not MIME decoding into separate /// segments but leaving it MIME encoded in one seg (headers and body). /// At runtime the segment is loaded and parsed into a temporary tree /// of LMail3Attachment objects. This flag is set for those temporary /// nodes. bool InMemoryOnly; public: LMail3Attachment(LMail3Store *store); ~LMail3Attachment(); const char *GetClass() override { return "LMail3Attachment"; } void SetInMemoryOnly(bool b); int64 GetId() { return SegId; } LMail3Attachment *Find(int64 Id); bool Load(LMail3Store::LStatement &s, int64 &ParentId); bool ParseHeaders() override; char *GetHeaders(); bool HasHeaders() { return ValidStr(Headers); } Store3CopyDecl; const char *GetStr(int id) override; Store3Status SetStr(int id, const char *str) override; int64 GetInt(int id) override; Store3Status SetInt(int id, int64 i) override; Store3Status Delete(bool ToTrash = false) override; LAutoStreamI GetStream(const char *file, int line) override; bool SetStream(LAutoStreamI stream) override; uint32_t Type() override { return MAGIC_ATTACHMENT; } bool IsOnDisk() override { return SegId > 0; } bool IsOrphan() override { return false; } uint64 Size() override; uint64 SizeChildren(); Store3Status Save(LDataI *Folder = NULL) override; void OnSave() override; }; class LMail3Mail : public LMail3Thing { LAutoString TextCache; LAutoString HtmlCache; LString IdCache; LAutoPtr SizeCache; LString InferredCharset; LString ErrMsg; void LoadSegs(); void OnSave() override; const char *GetTable() override { return MAIL3_TBL_MAIL; } const char *InferCharset(const char *ExampleTxt); bool Utf8Check(LString &v); bool Utf8Check(LVariant &v); bool Utf8Check(LAutoString &v); public: int Priority = MAIL_PRIORITY_NORMAL; int Flags = 0; int AccountId = 0; int64 MailSize = 0; uint32_t MarkColour = Rgba32(0, 0, 0, 0); // FIELD_MARK_COLOUR, 32bit rgba colour LString Subject; DIterator To; LMail3Attachment *Seg = NULL; Store3Addr From; Store3Addr Reply; LVariant Label; LVariant MessageID; LVariant References; LVariant FwdMsgId; LVariant BounceMsgId; LVariant ServerUid; LDateTime DateReceived; LDateTime DateSent; LMail3Mail(LMail3Store *store); ~LMail3Mail(); Store3CopyDecl; void SetStore(LMail3Store *s) override; bool Serialize(LMail3Store::LStatement &s, bool Write) override; const char *GetClass() override { return "LMail3Mail"; } LMail3Attachment *GetAttachment(int64 Id); bool FindSegs(const char *MimeType, LArray &Segs, bool Create = false); int GetAttachments(LArray *Lst = NULL); bool ParseHeaders() override; void ResetCaches(); uint32_t Type() override; uint64 Size() override; bool DbDelete() override; LDataStoreI *GetStore() override; LAutoStreamI GetStream(const char *file, int line) override; bool SetStream(LAutoStreamI stream) override; const char *GetStr(int id) override; Store3Status SetStr(int id, const char *str) override; int64 GetInt(int id) override; Store3Status SetInt(int id, int64 i) override; const LDateTime *GetDate(int id) override; Store3Status SetDate(int id, const LDateTime *t) override; LDataPropI *GetObj(int id) override; Store3Status SetObj(int id, LDataPropI *i) override; LDataIt GetList(int id) override; Store3Status SetRfc822(LStreamI *m) override; }; class LMail3Contact : public LMail3Thing { const char *GetTable() override { return MAIL3_TBL_CONTACT; } LHashTbl, LString*> f; public: LVariant Image; LDateTime DateMod; LMail3Contact(LMail3Store *store); ~LMail3Contact(); uint32_t Type() override { return MAGIC_CONTACT; } bool Serialize(LMail3Store::LStatement &s, bool Write) override; Store3CopyDecl; const char *GetClass() override { return "LMail3Contact"; } bool DbDelete() override; const char *GetStr(int id) override; Store3Status SetStr(int id, const char *str) override; int64 GetInt(int id) override { return -1; } LVariant *GetVar(int id) override; Store3Status SetVar(int id, LVariant *i) override; const LDateTime *GetDate(int id) override; Store3Status SetDate(int id, const LDateTime *i) override; }; class LMail3Group : public LMail3Thing { const char *GetTable() override { return MAIL3_TBL_GROUP; } LString Name; LString Group; LDateTime DateMod; public: LMail3Group(LMail3Store *store); ~LMail3Group(); uint32_t Type() override { return MAGIC_GROUP; } bool Serialize(LMail3Store::LStatement &s, bool Write) override; Store3CopyDecl; const char *GetClass() override { return "LMail3Group"; } bool DbDelete() override; const char *GetStr(int id) override; Store3Status SetStr(int id, const char *str) override; int64 GetInt(int id) override { return -1; } const LDateTime *GetDate(int id) override; Store3Status SetDate(int id, const LDateTime *i) override; }; class LMail3Filter : public LMail3Thing { int Index; int StopFiltering; int Direction; LString Name; LString ConditionsXml; LString ActionsXml; LString Script; LDateTime Modified; const char *GetTable() override { return MAIL3_TBL_FILTER; } public: LMail3Filter(LMail3Store *store); ~LMail3Filter(); uint32_t Type() override { return MAGIC_FILTER; } LDataStoreI *GetStore() override { return Store; } bool Serialize(LMail3Store::LStatement &s, bool Write) override; Store3CopyDecl; const char *GetClass() override { return "LMail3Filter"; } bool DbDelete() override; const char *GetStr(int id) override; Store3Status SetStr(int id, const char *str) override; int64 GetInt(int id) override; Store3Status SetInt(int Col, int64 n) override; const LDateTime *GetDate(int id) override; Store3Status SetDate(int id, const LDateTime *i) override; }; class LMail3CalendarFile : public LDataI { friend class LMail3Calendar; LMail3Store *Store = NULL; LMail3Calendar *Calendar = NULL; int64 Id = -1; int64 ParentId = -1; LString FileName; LString MimeType; LDateTime DateModified; - LString Data; + LString Uri; // Either 'Uri' or 'Data' must be valid + LString Data; // Store the data inline LString SizeCache; bool Dirty = false; public: LMail3CalendarFile(LMail3Store *store); const char *GetClass() override { return "LMail3CalendarFile"; } bool IsDirty() { return Dirty || Id < 0 || ParentId < 0; } bool CopyProps(LDataPropI &p) override; bool Serialize(LMail3Store::LStatement &s, bool Write); // Impl LDataPropI const char *GetStr(int id) override; Store3Status SetStr(int id, const char *str) override; const LDateTime *GetDate(int id) override; Store3Status SetDate(int id, const LDateTime *i) override; // Impl LDataI uint32_t Type() override { return MAGIC_CALENDAR_FILE; } bool IsOnDisk() override { return Id >= 0; } bool IsOrphan() override { return Calendar == NULL; } - LAutoStreamI GetStream(const char *file, int line) override { return LAutoStreamI(); } LDataStoreI *GetStore() override { return Store; } + + LAutoStreamI GetStream(const char *file, int line) override; uint64 Size() override; Store3Status Save(LDataI *Parent = NULL) override; Store3Status Delete(bool ToTrash = true) override; // Stubs int64 GetInt(int id) override { return 0; } Store3Status SetInt(int id, int64 i) override { return Store3NotImpl; } const LVariant *GetVar(int id) override { return NULL; } Store3Status SetVar(int id, LVariant *i) override { return Store3NotImpl; } LDataPropI *GetObj(int id) override { return NULL; } Store3Status SetObj(int id, LDataPropI *i) override { return Store3NotImpl; } LDataIt GetList(int id) override { EmptyVirtual(NULL); } }; class LMail3Calendar : public Store3CalendarObj { friend class LMail3CalendarFile; private: const char *GetTable() override { return MAIL3_TBL_CALENDAR; } DIterator Attachments; bool SaveAttachments(); public: LMail3Calendar(LMail3Store *store); ~LMail3Calendar(); LDataStoreI *GetStore() override { return Store; } bool SetId(int64_t id) override; bool Serialize(LMail3Store::LStatement &s, bool Write) override; const char *GetClass() override { return "LMail3Filter"; } bool DbDelete() override; LDataIt GetList(int id) override; }; #endif diff --git a/src/Store3Mail3/Mail3Calendar.cpp b/src/Store3Mail3/Mail3Calendar.cpp --- a/src/Store3Mail3/Mail3Calendar.cpp +++ b/src/Store3Mail3/Mail3Calendar.cpp @@ -1,413 +1,430 @@ #include "Mail3.h" #include "lgi/common/Json.h" LMail3Def TblCalendar[] = { {"Id", "INTEGER PRIMARY KEY AUTOINCREMENT"}, {"ParentId", "INTEGER"}, {"CalType", "INTEGER"}, // FIELD_CAL_TYPE {"Completed", "INTEGER"}, // FIELD_CAL_COMPLETED {"Start", "TEXT"}, // FIELD_CAL_START_UTC {"End", "TEXT"}, // FIELD_CAL_END_UTC {"TimeZone", "TEXT"}, // FIELD_CAL_TIMEZONE {"Subject", "TEXT"}, // FIELD_CAL_SUBJECT {"Location", "TEXT"}, // FIELD_CAL_LOCATION {"Uid", "TEXT"}, // FIELD_UID {"ShowTimeAs", "INTEGER"}, // FIELD_CAL_SHOW_TIME_AS {"Recur", "INTEGER"}, // FIELD_CAL_RECUR {"RecurFreq", "INTEGER"}, // FIELD_CAL_RECUR_FREQ {"RecurInterval", "INTEGER"}, // FIELD_CAL_RECUR_INTERVAL {"RecurEnd", "TEXT"}, // FIELD_CAL_RECUR_END_DATE {"RecurCount", "INTEGER"}, // FIELD_CAL_RECUR_END_COUNT {"RecurEndType", "INTEGER"}, // FIELD_CAL_RECUR_END_TYPE {"RecurPos", "TEXT"}, // FIELD_CAL_RECUR_FILTER_POS {"FilterDays", "INTEGER"}, // FIELD_CAL_RECUR_FILTER_DAYS {"FilterMonths", "INTEGER"}, // FIELD_CAL_RECUR_FILTER_MONTHS {"FilterYears", "TEXT"}, // FIELD_CAL_RECUR_FILTER_YEARS {"Notes", "TEXT"}, // FIELD_CAL_NOTES {"Colour", "INTEGER"}, // FIELD_COLOUR {"Guests", "TEXT"}, // FIELD_TO {"Reminders", "TEXT"}, // FIELD_CAL_REMINDERS {"LastCheck", "TEXT"}, // FIELD_CAL_LAST_CHECK {"AllDay", "INTEGER"}, // FIELD_CAL_ALL_DAY {"Status", "STATUS"}, // FIELD_CAL_STATUS {"DateModified", "TEXT"}, // FIELD_DATE_MODIFIED {0, 0} }; LMail3Def TblCalendarFiles[] = { {"Id", "INTEGER PRIMARY KEY AUTOINCREMENT"}, {"ParentId", "INTEGER"}, // ID of the calendar event... {"FileName", "TEXT"}, // FIELD_NAME {"MimeType", "TEXT"}, // FIELD_MIME_TYPE {"DateModified", "TEXT"}, // FIELD_DATE_MODIFIED + {"Uri", "TEXT"}, // FIELD_URI {"Data", "TEXT"}, // FIELD_ATTACHMENTS_DATA {0, 0} }; LMail3Calendar::LMail3Calendar(LMail3Store *store) { Store = store; } LMail3Calendar::~LMail3Calendar() { } bool LMail3Calendar::DbDelete() { Attachments.a.DeleteObjects(); Attachments.State = Store3Unloaded; // Delete any attachments. auto Sql = LString::Fmt("delete from " MAIL3_TBL_CALENDAR_FILES " where ParentId=" LPrintfInt64, Id); LMail3Store::LStatement FileDel(Store, Sql); if (!FileDel.Exec()) return false; // Delete the calendar itself Sql = LString::Fmt("delete from " MAIL3_TBL_CALENDAR " where Id=" LPrintfInt64, Id); LMail3Store::LStatement Del(Store, Sql); if (!Del.Exec()) return false; Id = -1; return true; } LDataIt LMail3Calendar::GetList(int id) { switch (id) { case FIELD_CAL_ATTACHMENTS: { if (Attachments.State == Store3Unloaded) { auto Sql = LString::Fmt("select * from %s where ParentId=" LPrintfInt64, MAIL3_TBL_CALENDAR_FILES, Id); LMail3Store::LStatement s(Store, Sql); if (s.IsOk()) { while (s.Row()) { if (auto a = new LMail3CalendarFile(Store)) { a->Calendar = this; if (a->Serialize(s, false)) Attachments.a.Add(a); else delete a; } } Attachments.State = Store3Loaded; } } return &Attachments; } } return NULL; } bool LMail3Calendar::SaveAttachments() { if (Id < 0) { LAssert(!"Needs an ID before you can call this."); return false; } printf("LMail3Calendar::Serialize attachments=%i\n", (int)Attachments.Length()); for (auto i: Attachments.a) { printf("LMail3Calendar::Serialize dirty=%i\n", i->IsDirty()); if (i->IsDirty()) { auto result = i->Save(this); printf("LMail3Calendar::Serialize result=%i\n", result); if (result != Store3Success) { LAssert(!"Attachment failed to save."); return false; } } } return true; } bool LMail3Calendar::SetId(int64_t id) { bool valid = Id >= 0; if (!LMail3Thing::SetId(id)) return false; if (!valid) SaveAttachments(); return true; } bool LMail3Calendar::Serialize(LMail3Store::LStatement &s, bool Write) { int i = 0; SERIALIZE_INT64(Id, i++); SERIALIZE_INT64(ParentId, i++); SERIALIZE_INT(CalType, i++); // FIELD_CAL_TYPE SERIALIZE_INT(Completed, i++); // FIELD_CAL_COMPLETED SERIALIZE_DATE(Start, i++); // FIELD_CAL_START_UTC SERIALIZE_DATE(End, i++); // FIELD_CAL_END_UTC SERIALIZE_LSTR(TimeZone, i++); // FIELD_CAL_TIMEZONE SERIALIZE_LSTR(Subject, i++); // FIELD_CAL_SUBJECT SERIALIZE_LSTR(Location, i++); // FIELD_CAL_LOCATION SERIALIZE_LSTR(Uid, i++); // FIELD_UID SERIALIZE_INT(ShowTimeAs, i++); // FIELD_CAL_SHOW_TIME_AS SERIALIZE_INT(Recur, i++); // FIELD_CAL_RECUR SERIALIZE_INT(RecurFreq, i++); // FIELD_CAL_RECUR_FREQ SERIALIZE_INT(RecurInterval, i++); // FIELD_CAL_RECUR_INTERVAL SERIALIZE_DATE(RecurEnd, i++); // FIELD_CAL_RECUR_END_DATE SERIALIZE_INT(RecurCount, i++); // FIELD_CAL_RECUR_END_COUNT SERIALIZE_INT(RecurEndType, i++); // FIELD_CAL_RECUR_END_TYPE SERIALIZE_LSTR(RecurPos, i++); // FIELD_CAL_RECUR_FILTER_POS SERIALIZE_INT(FilterDays, i++); // FIELD_CAL_RECUR_FILTER_DAYS SERIALIZE_INT(FilterMonths, i++); // FIELD_CAL_RECUR_FILTER_MONTHS SERIALIZE_LSTR(FilterYears, i++); // FIELD_CAL_RECUR_FILTER_YEARS SERIALIZE_LSTR(Notes, i++); // FIELD_CAL_NOTES SERIALIZE_COLOUR(Colour, i++); // FIELD_COLOUR SERIALIZE_LSTR(To, i++); // FIELD_TO SERIALIZE_LSTR(Reminders, i++); // FIELD_CAL_REMINDER_TIME SERIALIZE_DATE(LastCheck, i++); // FIELD_CAL_LAST_CHECK SERIALIZE_BOOL(AllDay, i++); // FIELD_CAL_ALL_DAY SERIALIZE_LSTR(EventStatus, i++); // FIELD_CAL_STATUS if (Write) { StoreStatus = Store3Success; if (Id >= 0 && !SaveAttachments()) { StoreStatus = Store3Error; return false; } } else if (To.Get()) { char c = To.Get()[0]; if (c != '[' && c != '{') { // Convert old style comma separated field to JSON LString s = To; To.Empty(); SetStr(FIELD_TO, s); } } return true; } /////////////////////////////////////////////////////////////////////////////////////////////// LMail3CalendarFile::LMail3CalendarFile(LMail3Store *store) : Store(store) { } bool LMail3CalendarFile::CopyProps(LDataPropI &p) { SetStr(FIELD_NAME, p.GetStr(FIELD_NAME)); SetStr(FIELD_MIME_TYPE, p.GetStr(FIELD_MIME_TYPE)); SetDate(FIELD_DATE_MODIFIED, p.GetDate(FIELD_DATE_MODIFIED)); + SetStr(FIELD_URI, p.GetStr(FIELD_URI)); SetStr(FIELD_ATTACHMENTS_DATA, p.GetStr(FIELD_ATTACHMENTS_DATA)); return true; } bool LMail3CalendarFile::Serialize(LMail3Store::LStatement &s, bool Write) { int i = 0; if (Write) LAssert(ParentId >= 0); SERIALIZE_INT64(Id, i++); SERIALIZE_INT64(ParentId, i++); SERIALIZE_LSTR(FileName, i++); // FIELD_NAME SERIALIZE_LSTR(MimeType, i++); // FIELD_MIME_TYPE SERIALIZE_DATE(DateModified, i++); // FIELD_DATE_MODIFIED + SERIALIZE_LSTR(Uri, i++); // FIELD_URI SERIALIZE_LSTR(Data, i++); // FIELD_ATTACHMENTS_DATA return true; } const char *LMail3CalendarFile::GetStr(int id) { switch (id) { case FIELD_NAME: return FileName; case FIELD_MIME_TYPE: return MimeType; + case FIELD_URI: + return Uri; case FIELD_ATTACHMENTS_DATA: return Data; case FIELD_SIZE: SizeCache = LFormatSize(Data.Length()); return SizeCache; } return NULL; } Store3Status LMail3CalendarFile::SetStr(int id, const char *str) { switch (id) { case FIELD_NAME: FileName = str; break; case FIELD_MIME_TYPE: MimeType = str; break; + case FIELD_URI: + Uri = str; + break; case FIELD_ATTACHMENTS_DATA: Data = str; break; default: return Store3NotImpl; } Dirty = true; return Store3Success; } const LDateTime *LMail3CalendarFile::GetDate(int id) { switch (id) { case FIELD_DATE_MODIFIED: return &DateModified; } return NULL; } Store3Status LMail3CalendarFile::SetDate(int id, const LDateTime *i) { switch (id) { case FIELD_DATE_MODIFIED: if (i) DateModified = *i; else DateModified.Empty(); break; default: return Store3NotImpl; } Dirty = true; return Store3Success; } +LAutoStreamI LMail3CalendarFile::GetStream(const char *file, int line) +{ + LAutoStreamI s; + if (Data) + s.Reset(new LMemStream(Data.Get(), Data.Length(), false)); + return s; +} + uint64 LMail3CalendarFile::Size() { return sizeof(this) + FileName.Length() + MimeType.Length() + sizeof(DateModified) + + Uri.Length() + Data.Length(); } Store3Status LMail3CalendarFile::Save(LDataI *Parent) { if (Parent) { Calendar = dynamic_cast(Parent); if (!Calendar) { LAssert(!"Wrong object type!"); return Store3Error; } if (Calendar->Attachments.IndexOf(this) < 0) Calendar->Attachments.Insert(this); } if (!Calendar) { LAssert(!"Must have parent calendar event."); return Store3Error; } ParentId = Calendar->Id; if (ParentId < 0) { // Parent event hasn't got an ID yet... assuming that when it's // saved, this object will get saved WITH it. return Store3Delayed; } LAutoPtr s; if (Id >= 0) s.Reset(new LMail3Store::LUpdate(Store, MAIL3_TBL_CALENDAR_FILES, Id)); else s.Reset(new LMail3Store::LInsert(Store, MAIL3_TBL_CALENDAR_FILES)); if (!s || !s->IsOk()) { LAssert(!"Query not valid."); return Store3Error; } if (!Serialize(*s, true)) { LAssert(!"Serialize failed"); return Store3Error; } if (!s->Exec()) { LAssert(!"Query failed"); return Store3Error; } if (Id < 0) Id = s->LastInsertId(); return Store3Success; } Store3Status LMail3CalendarFile::Delete(bool ToTrash) { if (!Calendar) { LAssert(!"No calendar object?"); return Store3Error; } auto idx = Calendar->Attachments.IndexOf(this); if (idx < 0) { LAssert(!"Calendar doesn't have this file?"); return Store3Error; } auto Sql = LString::Fmt("delete from " MAIL3_TBL_CALENDAR_FILES " where Id=" LPrintfInt64, Id); LMail3Store::LStatement s(Store, Sql); if (!s.Exec()) { LAssert(!"Delete query failed"); return Store3Error; } return Store3Success; }