Added CSV parsing and export of Symbian-format Event logs that have had their tables...
[qwerkisync] / EventTypes / PhoneCall.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 "PhoneCall.h"
20
21 #include "Attachment.h"
22 #include "DBBackends/RtcomEventLogger.h"
23 #include "EventParsers/CSVSymbianEventLogParser.h"
24 #include "NumberToNameLookup.h"
25 #include "Settings.h"
26
27 #include <QDateTime>
28 #include <QDir>
29 #include <QFile>
30 #include <QList>
31 #include <QRegExp>
32 #include <QString>
33 #include <QTextStream>
34
35 #include <utime.h>
36
37 #include <rtcom-eventlogger/event.h>
38 #include <rtcom-eventlogger/eventlogger-attach-iter.h>
39
40 using namespace EventTypes;
41
42 const DBBackends::iDBBackend &PhoneCall::DB() const
43 {
44         return DBBackends::RtcomEventLogger(CurrentSettings(), *this);
45 }
46
47 PhoneCall::PhoneCall(const Settings &settings) :
48         m_Settings(settings)
49 {
50         qDebug() << "Created new default Phone Call: " << *this;
51 }
52
53 PhoneCall::~PhoneCall()
54 {
55         foreach(QSharedPointer<Attachment> attachment, m_Attachments)
56         {
57                 attachment.clear();
58         }
59 }
60
61 PhoneCall::PhoneCall(const Settings &settings, const RTComElEvent &event, const QList<RTComElAttachment*> attachments) :
62         m_Settings(settings)
63 {
64         Destination(event.fld_outgoing ? Settings::OUTGOING : Settings::INCOMING);
65         Timestamp(QDateTime::fromTime_t(event.fld_start_time).toUTC());
66         DurationInSeconds(QDateTime::fromTime_t(event.fld_start_time).toUTC().secsTo(QDateTime::fromTime_t(event.fld_end_time).toUTC()));
67         Tel(event.fld_remote_uid);
68         if(Tel().indexOf("0") == 0)
69                 Tel(QString(Tel()).replace(QRegExp("^0"), "+44"));
70
71         if(attachments.count() > 0)
72                 foreach(RTComElAttachment *attachment, attachments)
73                         Attachments().append(new Attachment(*attachment));
74
75         qDebug() << "Created new Phone Call from RtCom:\n" << *this;
76 }
77
78 PhoneCall::PhoneCall(const Settings &settings, const Settings::eDirection destination, const QDateTime &timestamp, const QString &tel, const int durationInSeconds, const bool isMissedCall, const AttachmentCollection &attachments) :
79         m_Settings(settings), m_Destination(destination), m_Timestamp(timestamp), m_Tel(tel),
80         m_DurationInSeconds(durationInSeconds), m_IsMissedCall(isMissedCall), m_Attachments(attachments)
81 {
82         if(Tel().indexOf("0") == 0)
83                 Tel(QString(Tel()).replace(QRegExp("^0"), "+44"));
84
85         // TODO: Copy attachments.
86 //      if(attachments.count() > 0)
87 //              foreach(const QSharedPointer<Attachment *> &attachment, attachments)
88 //                      Attachments().append(attachment);
89
90         qDebug() << "Created new Phone Call: " << *this;
91 }
92
93 #include <QDebug>
94 const uint PhoneCall::HashCode() const
95 {
96 //      qDebug() << Timestamp().toUTC().toTime_t() << ", " << qHash(Tel()) << ", " << qHash(Destination()) << ", " << qHash(Contents()) << ", " << Attachments().HashCode();
97
98 //      foreach(QChar c, Contents().toUtf8())
99 //      {
100 //              qDebug() << c.unicode();
101 //      }
102
103         return
104                 Timestamp().toUTC().toTime_t() ^
105                 DurationInSeconds() ^
106                 qHash(Tel()) ^
107                 qHash(Destination()) ^
108                 Attachments().HashCode();
109 }
110
111 RTComElEvent * PhoneCall::toRTComEvent(const NumberToNameLookup &numberToNameLookup) const
112 {
113         QList<QString> voiceMailList;
114         voiceMailList << "+447945353070"; // T-Mobile UK Voicemail
115
116         QString groupId((Tel().length() < 7 || Tel().indexOf(QRegExp("[:alpha:]+")) > -1)
117                 ? Tel()
118                 : Tel().right(7));
119
120         RTComElEvent *event(rtcom_el_event_new());
121         memset(event, 0, sizeof(RTComElEvent));
122
123         RTCOM_EL_EVENT_SET_FIELD (event, service, g_strdup("RTCOM_EL_SERVICE_CALL"));
124         if(voiceMailList.contains(Tel()))
125                 RTCOM_EL_EVENT_SET_FIELD (event, event_type, g_strdup("RTCOM_EL_EVENTTYPE_CALL_VOICEMAIL"));
126         else if(IsMissedCall())
127                 RTCOM_EL_EVENT_SET_FIELD (event, event_type, g_strdup("RTCOM_EL_EVENTTYPE_CALL_MISSED"));
128         else
129                 RTCOM_EL_EVENT_SET_FIELD (event, event_type, g_strdup("RTCOM_EL_EVENTTYPE_CALL"));
130         RTCOM_EL_EVENT_SET_FIELD (event, start_time, Timestamp().toUTC().toTime_t());
131         RTCOM_EL_EVENT_SET_FIELD (event, end_time, Timestamp().addSecs(DurationInSeconds()).toUTC().toTime_t());
132         RTCOM_EL_EVENT_SET_FIELD (event, storage_time, Timestamp().addSecs(DurationInSeconds()).toUTC().toTime_t());
133         //RTCOM_EL_EVENT_SET_FIELD (event, is_read, 0);
134         RTCOM_EL_EVENT_SET_FIELD (event, outgoing, Destination() == Settings::OUTGOING ? 1 : 0);
135         //if(local_uid) // Voicemail local_uid transform needed here
136         RTCOM_EL_EVENT_SET_FIELD (event, local_uid, g_strdup("ring/tel/ring"));
137         //RTCOM_EL_EVENT_SET_FIELD (&event, local_name, g_strdup("<SelfHandle>"));
138         RTCOM_EL_EVENT_SET_FIELD (event, remote_uid, g_strdup(Tel().toUtf8()));
139         //RTCOM_EL_EVENT_SET_FIELD (&event, remote_name, g_strdup(QString::number(numberToNameLookup.ContactDetails().value(Tel()).second).toUtf8()));
140         RTCOM_EL_EVENT_SET_FIELD (event, remote_ebook_uid, g_strdup(QString::number(numberToNameLookup.ContactDetails().value(Tel()).first).toUtf8()));
141         RTCOM_EL_EVENT_SET_FIELD (event, group_uid, g_strdup(groupId.toUtf8()));
142
143         return event;
144 }
145
146 void PhoneCall::WriteCSVSymbian(QTextStream &stream, const ColumnIndicesByIndexHash &headerIndices, const QChar delimiter, const NumberToNameLookup &numberToNameLookup, SymbianEventLogStrings &strings) const
147 {
148         // 0|05/09/2007 11:25:12 am|1||||||<name>|3|8|0|0|<number>|||Unrecognized||
149         for(uint columnIndex(0); columnIndex < (uint)headerIndices.count(); ++columnIndex)
150         {
151                 const QString &heading(headerIndices.value(columnIndex));
152                 if("etype" == heading.toLower())
153                 {
154                         stream << "0"; // Phone calls are type '0'
155                 }
156                 else if("etime" == heading.toLower())
157                 {
158                         stream << Timestamp().toUTC().toString("dd/MM/yyyy h:mm:ss ap");
159                 }
160                 else if("remote" == heading.toLower())
161                 {
162                         stream << numberToNameLookup.ContactDetails().value(Tel()).second;
163                 }
164                 else if("direction" == heading.toLower())
165                 {
166                         if(IsMissedCall())
167                         {
168                                 if(!strings.contains("Missed Call"))
169                                         strings.insert("Missed Call", strings.count());
170                                 stream << strings.value("Missed Call");
171                         }
172                         else if(Settings::OUTGOING == Destination())
173                         {
174                                 if(!strings.contains("Outgoing"))
175                                         strings.insert("Outgoing", strings.count());
176                                 stream << strings.value("Outgoing");
177                         }
178                         else if (Settings::INCOMING == Destination())
179                         {
180                                 if(!strings.contains("Incoming"))
181                                         strings.insert("Incoming", strings.count());
182                                 stream << strings.value("Incoming");
183                         }
184                 }
185                 else if("duration" == heading.toLower())
186                 {
187                         stream << DurationInSeconds();
188                 }
189                 else if("dtype" == heading.toLower())
190                 {
191                         stream << "1"; // 1 seems to be KLogDurationValid
192                 }
193                 else if("status" == heading.toLower())
194                 {
195                         stream << "0"; // Always '0' for phone calls.
196                 }
197                 else if("subject" == heading.toLower())
198                 {
199                         // Subject seems to be ignored - this makes sense, but I
200                         // suspect that non-zero values are a bug that I can't duplicate...
201                         stream << "0";
202                 }
203                 else if("number" == heading.toLower())
204                 {
205                         stream << Tel();
206                 }
207                 else if("data" == heading.toLower())
208                 {
209                         // Event-specfic data - but not supported by DBU-SCV tool
210                         // Hex editing the DBU suggests there is SIP account info in here...
211                         // ...along the lines of:
212                         // "VOIP.URL=sip:<remote#>@<remote domain>.MA=sip:<my#>@<my domain>"
213                         stream << "Unrecognised";
214                 }
215                 else
216                 {
217                         // Don't print anything. Makes it obvious which fields we've
218                         // generated.
219                 }
220
221                 if(columnIndex < (uint)headerIndices.count() - 1)
222                         stream << delimiter;
223         }
224
225         stream << endl;
226 }
227
228 QDebug operator<<(QDebug dbg, PhoneCall& event)
229 {
230         dbg.nospace() << "\tHash:\t\t" << event.HashCode() << "\n";
231         dbg.nospace() << "\tDirection:\t\t" << (event.Destination() == Settings::OUTGOING ? "Made" : "Recieved") << "\n";
232         dbg.nospace() << "\tTimestamp:\t" << event.Timestamp().toUTC() << "\n";
233         dbg.nospace() << "\tDuration:\t\t" << event.DurationInSeconds() << " seconds\n";
234         dbg.nospace() << "\tRemote-Tel:\t" << event.Tel() << "\n";
235         //dbg.nospace() << "\tremote-name:\t" << event.fld_remote_name << "\n";
236
237         return dbg;
238 }