2 * Copyright (C) 2011, Jamie Thompson
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.
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.
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/>.
21 #include "Attachment.h"
23 #include "EventTypes/SMS.h"
27 #include <QTextStream>
31 using namespace EventParsers::VMGEntities;
33 VBody::VBody(const Settings &settings, const SMSEntity *parent) :
34 SMSEntity(settings, parent)
42 // Note: NametoNumberLookup is currently unused here.
43 void VBody::Write(QTextStream &stream, const EventTypes::SMS &event, const NumberToNameLookup &)
45 stream << "BEGIN:" << getTagName() << endl;
47 // First, the event's date field.
48 stream << "Date:" << event.Timestamp().toString("d.M.yyyy hh:mm:ss") << endl;
50 // ...next, the event's attachments
51 foreach(QSharedPointer<Attachment> attachment, event.Attachments())
52 stream << attachment->Stream().readAll() << endl;
54 // ...and now the event's contents
55 stream << event.Contents() << endl;
57 stream << "END:" << getTagName() << endl;
60 bool VBody::Read(const QString &initialLine, QTextStream &stream, EventTypes::SMS &event)
66 // Stream may or may not have a 'BEGIN' present. Swallow it if it's ours.
67 uint linePos(stream.pos());
68 QString lineData(initialLine.length() > 0 ? initialLine : stream.readLine());
69 if(lineData.startsWith("BEGIN:"))
71 if(lineData != QString("BEGIN:") + getTagName())
73 qDebug() << "Invalid stream";
83 // ...discard this line
84 lineData = stream.readLine();
91 if(lineData.startsWith("Date:"))
93 version = lineData.mid(lineData.indexOf(":")+1).toFloat();
95 else if(lineData.startsWith("BEGIN:"))
97 iReader* reader = Factory::Instantiate(CurrentSettings(), lineData, this);
98 bool valid(NULL != reader && reader->Read(lineData, stream, event));
101 // Quit processing if the nested content is not valid
105 else if(lineData.startsWith("END:"))
107 if(lineData != QString("END:") + getTagName())
109 qDebug() << getTagName() << " parser mismatch error: " << lineData;
120 if(text.isEmpty() && !lineData.isEmpty() && lineData.at(0) == 1)
122 // It's a binary SMS. Oh joy.
123 if(lineData.count() >= 5)
125 int wspMIMETypeID = lineData.at(5).unicode() & 0x7F;
126 if(BinaryMIMETypes().contains(wspMIMETypeID))
128 QString mimeType(BinaryMIMETypes().value(wspMIMETypeID));
129 qDebug() << "Attachment is: " << mimeType;
130 Attachment *binaryAttachment(new Attachment(
131 (QDir::tempPath() + "/attachment-" + QString::number(event.Timestamp().toTime_t()) + "-" + QString::number(event.Attachments().count()) + ".bin").toUtf8(),
136 // Alias the stream's device so we can asily access it
137 // directly in binary mode
138 QIODevice &binfile(*stream.device());
140 // Were going to ignore this data for the moment, but
141 // get an approximation of the range in the file.
142 int startPos(linePos);
144 int endPos(stream.pos());
145 binfile.seek(startPos);
147 qDebug() << "Binary offsets in file: " << startPos << ", " << endPos;
149 // Prepare our pointers for storing the data
150 int binDataLength((endPos - startPos) / sizeof(quint16));
151 char binaryData[binDataLength];
152 memset(&binaryData, 0, binDataLength);
153 char *binaryDataByte(binaryData);
155 // Grab the content from the file a byte at a time,
156 // skipping over every alternate byte (VMGs are UTF16LE)
157 // ...yes, even the binary data :(
158 int curpos(startPos);
159 while(binfile.pos() < endPos)
161 binfile.seek(curpos);
162 binfile.read(binaryDataByte++, 1);
164 // Look at the byte just stored. If it's an EOL,
165 // we're done. Even binary data ends on a valid line.
166 if(*(binaryDataByte - 1) == 0x0A)
168 // Note where the data actually ended.
169 binDataLength = (binfile.pos() - startPos) / sizeof(quint16);
173 qDebug() << hex << (int)binfile.pos() << "/" << hex << endPos << ": " << hex << (int)*(binaryDataByte - 1);
175 // Advance, skipping over every other byte.
176 curpos += sizeof(quint16);
178 // Move back a bit so the stream is ready to continue in
179 // text mode once we're done with the attachment.
180 stream.seek(endPos - 1);
182 // Directly access the output device and dump the binary
184 // NOTE: This is why the QFile needs to be unbuffered
185 binaryAttachment->Stream().device()->write(binaryData, binDataLength);
188 event.Attachments().append(binaryAttachment);
192 qDebug() << "Unrecognised binary mime type: " << hex << wspMIMETypeID;
193 return false; // Not supported for now
198 qDebug() << "Binary attachment too short (" << lineData.count() << " bytes). Not supported.";
199 return false; // Not supported for now
204 // If this isn't the first line, add a newline.
207 text.append(lineData);
211 linePos = stream.pos();
212 lineData = stream.readLine();
213 }while(!hasEnded && !stream.atEnd());
217 event.Contents(text);
218 //event.fld_storage_time