Added CSV parsing and export of Symbian-format Event logs that have had their tables...
[qwerkisync] / EventTypes / SMS.cpp
1 /*
2  * Copyright (C) 2011, Jamie Thompson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License as published by the Free Software Foundation; either
7  * version 3 of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; If not, see
16  * <http://www.gnu.org/licenses/>.
17  */
18
19 #include "SMS.h"
20
21 #include "Attachment.h"
22 #include "DBBackends/RtcomEventLogger.h"
23 #include "EventParsers/VMGEntities/VMessage.h"
24 #include "NumberToNameLookup.h"
25 #include "Settings.h"
26
27 #include <QRegExp>
28 #include <QString>
29 #include <QTextStream>
30
31 #include <rtcom-eventlogger/event.h>
32 #include <rtcom-eventlogger/eventlogger-attach-iter.h>
33
34 using namespace EventTypes;
35
36 const DBBackends::iDBBackend &SMS::DB() const
37 {
38         return DBBackends::RtcomEventLogger(CurrentSettings(), *this);
39 }
40
41 SMS::SMS(const Settings &settings) :
42         m_Settings(settings)
43 {
44 }
45
46 SMS::~SMS()
47 {
48         foreach(QSharedPointer<Attachment> attachment, m_Attachments)
49         {
50                 attachment.clear();
51         }
52 }
53
54 SMS::SMS(const Settings &settings, const RTComElEvent &event, const QList<RTComElAttachment*> attachments) :
55         m_Settings(settings)
56 {
57         Version(2.1);
58         IsRead(event.fld_is_read);
59         Destination(event.fld_outgoing ? SENT : INBOX);
60         Timestamp(QDateTime::fromTime_t(event.fld_start_time).toUTC());
61         Tel(QString::fromUtf8(event.fld_remote_uid));
62         if(Tel().indexOf("0") == 0)
63                 Tel(QString(Tel()).replace(QRegExp("^0"), "+44"));
64         //Contents(QString(event.fld_free_text).replace("\n", QChar(0x2029)));
65         Contents(QString::fromUtf8(event.fld_free_text));
66         if(event.fld_flags != 0)
67         {
68                 qDebug() << "fld_flags: = " << event.fld_flags;
69         }
70         Pending(event.fld_flags & GetFlagValue("RTCOM_EL_FLAG_SMS_PENDING"));
71
72         // We directly access the m_Attachments member variable here rather than the
73         // accessor as the accessor is const
74         if(attachments.count() > 0)
75                 foreach(RTComElAttachment *attachment, attachments)
76                         m_Attachments.append(new Attachment(*attachment));
77 }
78 #include <QDebug>
79 const uint SMS::HashCode() const
80 {
81 //      qDebug() << Timestamp().toUTC().toTime_t() << ", " << qHash(Tel()) << ", " << qHash(Destination()) << ", " << qHash(Contents()) << ", " << Attachments().HashCode();
82
83 //      foreach(QChar c, Contents().toUtf8())
84 //      {
85 //              qDebug() << c.unicode();
86 //      }
87
88         return
89                 Timestamp().toUTC().toTime_t() ^
90                 qHash(Tel()) ^
91                 qHash(Destination()) ^
92                 qHash(Contents()) ^
93                 Attachments().HashCode();
94 }
95
96 RTComElEvent * SMS::toRTComEvent(const NumberToNameLookup &numberToNameLookup) const
97 {
98         QString groupId((Tel().length() < 7 || Tel().indexOf(QRegExp("[:alpha:]+")) > -1)
99                 ? Tel()
100                 : Tel().right(7));
101
102         RTComElEvent *event(rtcom_el_event_new());
103         memset(event, 0, sizeof(RTComElEvent));
104
105         RTCOM_EL_EVENT_SET_FIELD (event, service, g_strdup("RTCOM_EL_SERVICE_SMS"));
106         RTCOM_EL_EVENT_SET_FIELD (event, event_type, g_strdup("RTCOM_EL_EVENTTYPE_SMS_MESSAGE"));
107         RTCOM_EL_EVENT_SET_FIELD (event, storage_time, Timestamp().toUTC().toTime_t());
108         RTCOM_EL_EVENT_SET_FIELD (event, start_time, Timestamp().toUTC().toTime_t());
109         RTCOM_EL_EVENT_SET_FIELD (event, end_time, Timestamp().toUTC().toTime_t());
110         RTCOM_EL_EVENT_SET_FIELD (event, is_read, IsRead() ? 1 : 0);
111         RTCOM_EL_EVENT_SET_FIELD (event, outgoing, Destination() == SMS::SENT ? 1 : 0);
112         RTCOM_EL_EVENT_SET_FIELD (event, local_uid, g_strdup("ring/tel/ring"));
113         //RTCOM_EL_EVENT_SET_FIELD (&event, local_name, g_strdup("<SelfHandle>"));
114         RTCOM_EL_EVENT_SET_FIELD (event, remote_uid, g_strdup(Tel().toUtf8()));
115         //RTCOM_EL_EVENT_SET_FIELD (&event, remote_name, g_strdup(QString::number(numberToNameLookup.ContactDetails().value(Tel()).second).toUtf8()));
116         RTCOM_EL_EVENT_SET_FIELD (event, remote_ebook_uid, g_strdup(QString::number(numberToNameLookup.ContactDetails().value(Tel()).first).toUtf8()));
117         RTCOM_EL_EVENT_SET_FIELD (event, group_uid, g_strdup(groupId.toUtf8()));
118         //RTCOM_EL_EVENT_SET_FIELD (event, free_text, g_strdup(Contents().replace(0x2029, "\n").toUtf8()));
119         RTCOM_EL_EVENT_SET_FIELD (event, free_text, g_strdup(Contents().toUtf8()));
120
121         return event;
122 }
123
124 void SMS::WriteCSVSymbian(QTextStream &stream, const ColumnIndicesByIndexHash &headerIndices, const QChar delimiter, const NumberToNameLookup &numberToNameLookup, SymbianEventLogStrings &strings) const
125 {
126         // 0|05/09/2007 11:25:12 am|1||||||<name>|3|8|0|0|<number>|||Unrecognized||
127         for(uint columnIndex(0); columnIndex < headerIndices.count(); ++columnIndex)
128         {
129                 const QString &heading(headerIndices.value(columnIndex));
130                 if("etype" == heading.toLower())
131                 {
132                         stream << "3"; // SMSes are type '3'
133                 }
134                 else if("etime" == heading.toLower())
135                 {
136                         stream << Timestamp().toUTC().toString("dd/MM/yyyy h:mm:ss ap");
137                 }
138                 else if("remote" == heading.toLower())
139                 {
140                         stream << numberToNameLookup.ContactDetails().value(Tel()).second;
141                 }
142                 else if("direction" == heading.toLower())
143                 {
144                         if(Settings::OUTGOING == Destination())
145                         {
146                                 if(!strings.contains("Outgoing"))
147                                         strings.insert("Outgoing", strings.count());
148                                 stream << strings.value("Outgoing");
149                         }
150                         else if (Settings::INCOMING == Destination())
151                         {
152                                 if(!strings.contains("Incoming"))
153                                         strings.insert("Incoming", strings.count());
154                                 stream << strings.value("Incoming");
155                         }
156                 }
157                 else if("duration" == heading.toLower())
158                 {
159                         stream << "0"; // SMSes are always 0
160                 }
161                 else if("dtype" == heading.toLower())
162                 {
163                         stream << "-1"; // -1 seems to match KLogDurationNone
164                 }
165                 else if("status" == heading.toLower())
166                 {
167                         stream << "0"; // Always '0' for phone calls.
168                 }
169                 else if("subject" == heading.toLower())
170                 {
171                         // Subject seems to be ignored - this makes sense, but I
172                         // suspect that non-zero values are a bug that I can't duplicate...
173                         stream << "0";
174                 }
175                 else if("number" == heading.toLower())
176                 {
177                         stream << Tel();
178                 }
179                 else if("data" == heading.toLower())
180                 {
181                         // Event-specfic data - but not supported by DBU-SCV tool
182                         // Hex editing the DBU suggests there is SIP account info in here...
183                         // ...along the lines of:
184                         // "VOIP.URL=sip:<remote#>@<remote domain>.MA=sip:<my#>@<my domain>"
185                         stream << "Unrecognised";
186                 }
187                 else
188                 {
189                         // Don't print anything. Makes it obvious which fields we've
190                         // generated.
191                 }
192
193                 if(columnIndex < headerIndices.count() - 1)
194                         stream << delimiter;
195         }
196
197         stream << endl;
198 }
199
200 const QString SMS::PathForVMG() const
201 {
202         QString eventPath("/sms");
203         eventPath += Destination() == EventTypes::SMS::SENT ? "/Sent/" : "/Inbox/";
204         eventPath += QString::number(Timestamp().toUTC().date().year()) + "/";
205
206         return eventPath;
207 }
208
209 void SMS::WriteVMG(QTextStream &stream, const NumberToNameLookup &numberToNameLookup) const
210 {
211         EventParsers::VMGEntities::VMessage writer(CurrentSettings(), NULL, 1.1);
212         writer.Write(stream, *this, numberToNameLookup);
213         stream.flush();
214 }
215
216 QDebug operator<<(QDebug dbg, SMS& event)
217 {
218         dbg.nospace() << "\tFolder:\t\t" << (event.Destination() == SMS::SENT ? "Sent" : "Inbox") << "\n";
219         dbg.nospace() << "\tTimestamp:\t" << event.Timestamp().toUTC() << "\n";
220         dbg.nospace() << "\tRemote-Tel:\t" << event.Tel() << "\n";
221         //dbg.nospace() << "\tremote-name:\t" << event.fld_remote_name << "\n";
222         dbg.nospace() << "\tIs-read:\t\t" << (event.IsRead() ? "true" : "false") << "\n";
223         dbg.nospace() << "\tContents:\t" << event.Contents() << "\n";
224
225         return dbg;
226 }