[atchat] Do not add an \r to the wakeup command to avoid many issues
[qtopia.git] / src / libraries / qtopiaphone / qsmsmessage.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2000-2008 TROLLTECH ASA. All rights reserved.
4 **
5 ** This file is part of the Opensource Edition of the Qtopia Toolkit.
6 **
7 ** This software is licensed under the terms of the GNU General Public
8 ** License (GPL) version 2.
9 **
10 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
11 **
12 ** Contact info@trolltech.com if any conditions of this licensing are
13 ** not clear to you.
14 **
15 **
16 **
17 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 **
20 ****************************************************************************/
21
22 #include <stdlib.h>
23
24 #include <qsmsmessage.h>
25 #ifdef PHONESIM
26 #include "qsmsmessage_p.h"
27 #else
28 #include <qtopiaphone/private/qsmsmessage_p.h>
29 #endif
30 #include <qatutils.h>
31 #include <qgsmcodec.h>
32 #ifdef Q_WS_QWS
33 #include <qtopialog.h>
34 #else
35 #include <qdebug.h>
36 #define qLog(dbgcat) if(1); else qDebug()
37 #endif
38 #include <qstringlist.h>
39
40 #include <qtextcodec.h>
41
42 const int Unit = 1;
43
44 class QSMSMessagePartPrivate
45 {
46 public:
47     QSMSMessagePartPrivate( const QString& text )
48     {
49         this->isText = true;
50         this->text = text;
51         this->position = 0;
52     }
53     QSMSMessagePartPrivate( const QString& mimeType, const QByteArray& data )
54     {
55         this->isText = false;
56         this->mimeType = mimeType;
57         this->data = data;
58         this->position = 0;
59     }
60     QSMSMessagePartPrivate( const QString& mimeType, const QByteArray& data, uint position )
61     {
62         this->isText = false;
63         this->mimeType = mimeType;
64         this->data = data;
65         this->position = position;
66     }
67     QSMSMessagePartPrivate( const QSMSMessagePartPrivate& part )
68     {
69         this->isText = part.isText;
70         this->text = part.text;
71         this->mimeType = part.mimeType;
72         this->data = part.data;
73         this->position = part.position;
74     }
75
76     bool isText;
77     QString text;
78     QString mimeType;
79     QByteArray data;
80     uint position;
81 };
82
83 class QSMSMessagePrivate
84 {
85 public:
86     QSMSMessagePrivate()
87     {
88 #if QT_VERSION < 0x040400
89         ref.init(1);
90 #else
91         ref = 1;
92 #endif
93         mValidity = 2 * 24 * 60;        // 2 days
94         mReplyRequest = false;
95         mStatusReportRequested = false;
96         mMessageType = QSMSMessage::Normal;
97         mCodec = 0;
98         mForceGsm = false;
99         mBestScheme = (QSMSDataCodingScheme)(-1);
100         mDataCodingScheme = -1;
101         mMessageClass = -1;
102         mProtocol = 0;
103     }
104
105     ~QSMSMessagePrivate()
106     {
107     }
108
109     /*  Convert from minutes to GSM 03.40 specification (section 9.2.3.12).
110          - 0-143   = 0 to 12 hours in 5 minute increments (0 = 5 minutes).
111          - 144-167 = 12hrs30min to 24hrs in 30 minute increments.
112          - 168-196 = 2days to 30days in 1 day increments.
113          - 197-255 = 5weeks to 63weeks in 1 week increments.
114     */
115     uint gsmValidityPeriod()
116     {
117         uint mins = mValidity;
118
119         if ( mins < 5 )
120             return 0;
121
122         if ( mins <= 12 * 60 )
123             return ((mins / 5) - 1);
124
125         if ( mins <= 24 * 60 )
126             return ((mins + 29 - (12 * 60 + 30)) / 30) + 144;
127
128         uint days = (mins + (24 * 60 - 1)) / (24 * 60);
129
130         if ( days <= 30 )
131             return days - 2 + 168;
132
133         if ( days <= 63 * 7 )
134             return ((days + 6) / 7) - 5 + 197;
135
136         return 255;
137     }
138
139     void setGsmValidityPeriod(uint value)
140     {
141         if ( value <= 143 )
142             mValidity = (value + 1) * 5;
143         else if ( value <= 167 )
144             mValidity = (value - 143) * 30 + 12 * 60;
145         else if ( value <= 196 )
146             mValidity = (value - 166) * 24 * 60;
147         else
148             mValidity = (value - 192) * 7 * 24 * 60;
149     }
150
151     template <typename Stream> void readFromStream( Stream &s )
152     {
153         QString codec;
154         s >> codec;
155         if ( codec.length() > 0)
156             mCodec = QTextCodec::codecForName( codec.toLatin1() );
157         else
158             mCodec = 0;
159         s >> mServiceCenter;
160         s >> mRecipient;
161         s >> mSender;
162         s >> mTimestamp;
163         s >> mValidity;
164         int val;
165         s >> val;
166         mReplyRequest = (val != 0);
167         s >> val;
168         mStatusReportRequested = (val != 0);
169         s >> val;
170         mForceGsm = (val != 0);
171         s >> val;
172         mMessageType = (QSMSMessage::MessageType)val;
173         s >> val;
174         mBestScheme = (QSMSDataCodingScheme)val;
175         s >> mHeaders;
176         s >> mParts;
177         mCachedBody = QString();
178         s >> mDataCodingScheme;
179         s >> mMessageClass;
180         s >> mProtocol;
181     }
182
183     template <typename Stream> void writeToStream( Stream &s )
184     {
185         if ( mCodec )
186             s << QString( mCodec->name() );
187         else
188             s << QString( "" );
189         s << mServiceCenter;
190         s << mRecipient;
191         s << mSender;
192         s << mTimestamp;
193         s << mValidity;
194         s << (int)mReplyRequest;
195         s << (int)mStatusReportRequested;
196         s << (int)mForceGsm;
197         s << (int)mMessageType;
198         s << (int)mBestScheme;
199         s << mHeaders;
200         s << mParts;
201         s << mDataCodingScheme;
202         s << mMessageClass;
203         s << mProtocol;
204     }
205
206     void copy( QSMSMessagePrivate *from )
207     {
208         *this = *from;
209         mHeaders = from->mHeaders;
210     }
211
212 #if QT_VERSION < 0x040400
213     QBasicAtomic ref;
214 #else
215     QAtomicInt ref;
216 #endif
217     QTextCodec *mCodec;
218     QString mServiceCenter;
219     QString mRecipient;
220     QString mSender;
221     QDateTime mTimestamp;
222     uint mValidity;
223     bool mReplyRequest;
224     bool mStatusReportRequested;
225     bool mForceGsm;
226     QSMSMessage::MessageType mMessageType;
227     QSMSDataCodingScheme mBestScheme;
228     QByteArray mHeaders;
229     QList<QSMSMessagePart> mParts;
230     QString mCachedBody;
231     int mDataCodingScheme;
232     int mMessageClass;
233     int mProtocol;
234 };
235
236 /*!
237     \enum QSMSMessage::MessageType
238     Defines the type of an SMS message.
239
240     \value Normal The message is a normal SMS message.
241     \value CellBroadCast The message is a cell broadcast message.
242     \value StatusReport The message is an SMS status report message.
243 */
244
245 /*!
246     \enum QSMSDataCodingScheme
247     Define the data coding scheme to use to encode SMS message text.
248
249     \value QSMS_Compressed Flag that indicates that the data is compressed
250            (not used at present).
251     \value QSMS_MessageClass Flag that indicates the message class
252            (not used at present).
253     \value QSMS_DefaultAlphabet Use the default 7-bit GSM alphabet.
254     \value QSMS_8BitAlphabet Use the locale-specific 8-bit alphabet.
255     \value QSMS_UCS2Alphabet Use the UCS-2 16-bit alphabet.
256     \value QSMS_ReservedAlphabet Use the reserved alphabet
257            (not used at present).
258 */
259
260 /*!
261     \class QSMSMessagePart
262     \mainclass
263     \brief The QSMSMessagePart class specifies a part within an SMS message.
264
265     \ingroup telephony
266     \sa QSMSMessage
267
268     QSMSMessage objects contain zero or more "parts", which describe
269     the contents of the SMS message.  A part may be plain text,
270     or a binary stream tagged by a MIME type.
271 */
272
273 /*!
274     Constructs an empty SMS message part.
275 */
276 QSMSMessagePart::QSMSMessagePart()
277 {
278     d = new QSMSMessagePartPrivate( QString() );
279 }
280
281 /*!
282     Constructs a new plain text SMS message part from the
283     string \a text.
284 */
285 QSMSMessagePart::QSMSMessagePart( const QString& text )
286 {
287     d = new QSMSMessagePartPrivate( text );
288 }
289
290 /*!
291     Constructs a new binary SMS message part with the specified
292     \a mimeType and \a data.
293 */
294 QSMSMessagePart::QSMSMessagePart( const QString& mimeType, const QByteArray& data )
295 {
296     d = new QSMSMessagePartPrivate( mimeType, data );
297 }
298
299 /*!
300     Constructs a new binary SMS message part with the specified
301     \a mimeType and \a data.  The part is intended to be displayed
302     at \a position within a subsequent text part.
303 */
304 QSMSMessagePart::QSMSMessagePart( const QString& mimeType, const QByteArray& data, uint position )
305 {
306     d = new QSMSMessagePartPrivate( mimeType, data, position );
307 }
308
309 /*!
310     Constructs a copy of \a part.
311 */
312 QSMSMessagePart::QSMSMessagePart( const QSMSMessagePart& part )
313 {
314     d = new QSMSMessagePartPrivate( *(part.d) );
315 }
316
317 /*!
318     Destructs the QSMSMessagePart.
319 */
320 QSMSMessagePart::~QSMSMessagePart()
321 {
322     delete d;
323 }
324
325 /*!
326     Assigns a copy of \a part to this object.
327 */
328 QSMSMessagePart& QSMSMessagePart::operator=( const QSMSMessagePart& part )
329 {
330     if ( &part != this ) {
331         delete d;
332         d = new QSMSMessagePartPrivate( *(part.d) );
333     }
334     return *this;
335 }
336
337 /*!
338     Returns true if this SMS message part is plain text; or false if binary.
339 */
340 bool QSMSMessagePart::isText() const
341 {
342     return d->isText;
343 }
344
345 /*!
346     Returns the plain text contents of this SMS message part,
347     or QString() if it is not a text part.
348 */
349 QString QSMSMessagePart::text() const
350 {
351     return d->text;
352 }
353
354 /*!
355     Returns the MIME type associated with this SMS message part,
356     or QString() if it is not a binary part.
357 */
358 QString QSMSMessagePart::mimeType() const
359 {
360     return d->mimeType;
361 }
362
363 /*!
364     Returns the binary data associated with this SMS message part.
365     Returns and empty QByteArray if it is not a binary part.
366 */
367 const QByteArray& QSMSMessagePart::data() const
368 {
369     return d->data;
370 }
371
372 /*!
373     Returns the text position to display this SMS message part at.
374 */
375 uint QSMSMessagePart::position() const
376 {
377     return d->position;
378 }
379
380 /*!
381     \internal
382     \fn void QSMSMessagePart::deserialize(Stream &stream)
383 */
384 template <typename Stream> void QSMSMessagePart::deserialize(Stream &stream)
385 {
386     int flag;
387     stream >> flag;
388     d->isText = (flag != 0);
389     stream >> d->text;
390     stream >> d->mimeType;
391     stream >> d->data;
392     stream >> d->position;
393 }
394
395 /*!
396     \internal
397     \fn void QSMSMessagePart::serialize(Stream &stream) const
398 */
399 template <typename Stream> void QSMSMessagePart::serialize(Stream &stream) const
400 {
401     stream << (int)(d->isText);
402     stream << d->text;
403     stream << d->mimeType;
404     stream << d->data;
405     stream << d->position;
406 }
407
408 /*!
409     \class QSMSMessage
410     \mainclass
411     \brief The QSMSMessage class specifies the contents of an SMS message.
412
413     This class is intended for use with QSMSReader and QSMSSender to process
414     SMS messages according to 3GPP TS 03.40 and 23.040.
415
416     An incoming SMS message from QSMSReader will typically have text() and
417     sender() set.  Other fields such as destinationPort() and
418     applicationData() may be set if the message is a WAP Push or SMS datagram
419     message rather than plain text.
420
421     An outgoing SMS message sent via QSMSSender will need to have at least
422     recipient() and text() set.  If the message is a WAP Push or SMS datagram
423     message rather than plain text, then destinationPort() and applicationData()
424     should also be set.
425
426     Special header fields in SMS messages can be accessed with serviceCenter(),
427     replyRequest(), statusReportRequested(), validityPeriod(), timestamp(),
428     dataCodingScheme(), and protocol().
429
430     \ingroup telephony
431 */
432
433 /*!
434     Constructs an empty QSMSMessage.
435 */
436 QSMSMessage::QSMSMessage()
437 {
438     d = new QSMSMessagePrivate;
439 }
440
441 /*!
442     Constructs an QSMSMessage that is a copy of \a msg.
443 */
444 QSMSMessage::QSMSMessage(const QSMSMessage &msg)
445 {
446     d = msg.d;
447     d->ref.ref();
448 }
449
450 /*!
451     Destructs the QSMSMessage.
452 */
453 QSMSMessage::~QSMSMessage()
454 {
455     if ( !d->ref.deref() )
456         delete d;
457 }
458
459 QSMSMessagePrivate *QSMSMessage::dwrite()
460 {
461     // If we are the only user of the private object, return it as-is.
462     if ( d->ref == 1 )
463         return d;
464
465     // Create a new private object and copy the current contents into it.
466     QSMSMessagePrivate *newd = new QSMSMessagePrivate();
467     newd->copy( d );
468     if ( !d->ref.deref() )
469         delete d;
470     d = newd;
471     return newd;
472 }
473
474 /*!
475     Assigns a copy of \a msg to this object.
476 */
477 QSMSMessage& QSMSMessage::operator=( const QSMSMessage &msg)
478 {
479     if ( d == msg.d )
480         return *this;
481
482     if ( !d->ref.deref() )
483         delete d;
484
485     d = msg.d;
486     d->ref.ref();
487
488     return *this;
489 }
490
491 /*!
492     Sets the contents of this QSMSMessage to a single plain text
493     body containing \a str.  This is for simple messages only.
494     Complex messages should be constructed part by part using
495     the addPart() method.
496
497     \sa text()
498 */
499 void QSMSMessage::setText(const QString &str)
500 {
501     clearParts();
502     addPart( QSMSMessagePart( str ) );
503 }
504
505 /*!
506     Returns the contents of this QSMSMessage as a single plain text string.
507     If the message contains binary parts, they will not appear
508     in the result.  This is for simple message viewers only.
509     Complex message viewers should iterate over the list returned
510     by parts().
511
512     \sa setText()
513 */
514 QString QSMSMessage::text() const
515 {
516     // Handle the easy cases first.
517     if ( d->mParts.count() == 0 ) {
518         return QString();
519     } else if ( d->mParts.count() == 1 && d->mParts[0].isText() ) {
520         return d->mParts[0].text();
521     } else if ( d->mCachedBody.length() > 0 ) {
522         return d->mCachedBody;
523     }
524
525     // We need the private structure to be writable to cache the body.
526     const_cast<QSMSMessage *>(this)->dwrite();
527
528     // Append all text parts to get the complete body.
529     QString body = QString();
530     for ( int posn = 0; posn < d->mParts.count(); ++posn ) {
531         if ( d->mParts[posn].isText() ) {
532             body += d->mParts[posn].text();
533         }
534     }
535     d->mCachedBody = body;
536     return body;
537 }
538
539 /*!
540     If the message consists solely of characters in the 7-bit GSM
541     encoding, then the message will be transmitted that way.
542     Otherwise \a codec is used to convert the characters into an
543     appropriate language-specific 8-bit encoding.  If \a codec is
544     set to NULL, then the default UCS-2 encoding for GSM messages
545     is used.
546
547     \sa textCodec()
548 */
549 void QSMSMessage::setTextCodec(QTextCodec *codec)
550 {
551     dwrite()->mCodec = codec;
552 }
553
554 /*!
555     Returns the current 8-bit text codec, or NULL if none has
556     been set.
557
558     \sa setTextCodec()
559 */
560 QTextCodec *QSMSMessage::textCodec() const
561 {
562     return d->mCodec;
563 }
564
565 /*!
566     If \a force is true, then the codec set by QSMSMessage::setTextCodec
567     is ignored and the 7-bit GSM encoding is always used.  Setting this
568     flag increases the number of characters that can be sent in any
569     given SMS message, but may lose information.
570
571     \sa forceGsm()
572 */
573 void QSMSMessage::setForceGsm(bool force)
574 {
575     dwrite()->mForceGsm = force;
576 }
577
578 /*!
579     Returns true if the 7-bit GSM encoding has been forced.
580
581     \sa setForceGsm()
582 */
583 bool QSMSMessage::forceGsm() const
584 {
585     return d->mForceGsm;
586 }
587
588 /*!
589     Sets the SMS data coding \a scheme to use for this message.
590     Normally you won't need to call this unless the user has
591     somehow explicitly requested an override.  By default,
592     the QSMSMessage class will choose the best scheme for you.
593     Should be set to one of \c QSMS_DefaultAlphabet,
594     \c QSMS_8BitAlphabet, or \c QSMS_UCS2Alphabet.
595
596     \sa bestScheme()
597 */
598 void QSMSMessage::setBestScheme(QSMSDataCodingScheme scheme)
599 {
600     dwrite()->mBestScheme = scheme;
601 }
602
603
604 /*!
605     Returns the best SMS data coding scheme to use for this
606     message, determined by an inspection of the plain text body parts.
607
608     \sa setBestScheme()
609 */
610 QSMSDataCodingScheme QSMSMessage::bestScheme() const
611 {
612     QTextCodec *codec = QAtUtils::codec( "gsm-noloss" );
613     QString body = text();
614     uint len = body.length();
615     bool gsmSafe;
616
617     // Did the user provide a scheme override?
618     if ( d->mBestScheme != (QSMSDataCodingScheme)(-1) )
619         return d->mBestScheme;
620
621     // Encode zero-length bodies in the default alphabet.
622     if ( len == 0 )
623         return QSMS_DefaultAlphabet;
624
625     // Always use GSM if we are forced to do so.
626     if ( d->mForceGsm )
627         return QSMS_DefaultAlphabet;
628
629     // Check the body for non-GSM characters.
630     gsmSafe = codec->canEncode( body );
631
632     // Use the default alphabet if everything is GSM-compatible.
633     if ( gsmSafe )
634         return QSMS_DefaultAlphabet;
635
636     // See if we can convert to 8-bit using the codec
637     // without losing any information.
638     if ( d->mCodec && d->mCodec->canEncode( body ) )
639         return QSMS_8BitAlphabet;
640
641     // Default to the UCS-2 alphabet.
642     return QSMS_UCS2Alphabet;
643 }
644
645 /*!
646     Sets the recipient's telephone number to \a txt.
647
648     \sa recipient()
649 */
650 void QSMSMessage::setRecipient(const QString &txt)
651 {
652     dwrite()->mRecipient = txt;
653 }
654
655 /*!
656     Returns the recipient's telephone number.  Normally QString()
657     for an incoming message.
658
659     \sa setRecipient()
660 */
661 QString QSMSMessage::recipient() const
662 {
663     return d->mRecipient;
664 }
665
666 /*!
667     Sets the sender's telephone number to \a txt.
668
669     \sa sender()
670 */
671 void QSMSMessage::setSender(const QString &txt)
672 {
673     dwrite()->mSender = txt;
674 }
675
676 /*!
677     Returns the sender's telephone number.  Normally QString()
678     for an outgoing message.
679
680     \sa setSender()
681 */
682 QString QSMSMessage::sender() const
683 {
684     return d->mSender;
685 }
686
687 /*!
688     Sets the service center to use for transmitting this SMS message,
689     or QString() for the default service center, to \a str.
690
691     \sa serviceCenter()
692 */
693 void QSMSMessage::setServiceCenter(const QString &str)
694 {
695     dwrite()->mServiceCenter = str;
696 }
697
698 /*!
699     Returns the service center.
700
701     \sa setServiceCenter()
702 */
703 QString QSMSMessage::serviceCenter() const
704 {
705     return d->mServiceCenter;
706 }
707
708 /*!
709     Enable or disable the "reply request" flag for this SMS message,
710     according to the value of \a on.
711
712     \sa replyRequest()
713 */
714 void QSMSMessage::setReplyRequest(bool on )
715 {
716     dwrite()->mReplyRequest = on;
717 }
718
719 /*!
720     Returns the "reply request" flag.
721
722     \sa setReplyRequest()
723 */
724 bool QSMSMessage::replyRequest() const
725 {
726     return d->mReplyRequest;
727 }
728
729 /*!
730     Sets the status report requested flag to \a on.
731
732     \sa statusReportRequested()
733 */
734 void QSMSMessage::setStatusReportRequested(bool on)
735 {
736     dwrite()->mStatusReportRequested = on;
737 }
738
739 /*!
740     Returns true if status report requested flag is currently set;otherwise returns false.
741
742     \sa setStatusReportRequested()
743 */
744 bool QSMSMessage::statusReportRequested() const
745 {
746     return d->mStatusReportRequested;
747 }
748
749 /*!
750     Sets the validity period to \a minutes.  The default is 2 days.  If the value
751     is set to \c{(uint)(-1)}, it indicates that the message should have no
752     validity period specified.
753
754     \sa validityPeriod(), gsmValidityPeriod()
755 */
756 void QSMSMessage::setValidityPeriod(uint minutes)
757 {
758     dwrite()->mValidity = minutes;
759 }
760
761 /*!
762     Returns the validity period in minutes for this message.  The default is 2 days.
763     If the value is \c{(uint)(-1)}, it indicates that the message should have no
764     validity period specified.
765
766     \sa setValidityPeriod(), setGsmValidityPeriod()
767 */
768 uint QSMSMessage::validityPeriod() const
769 {
770     return d->mValidity;
771 }
772
773 /*!
774     Sets the GSM validity period to \a value, which must be between
775     0 and 255, inclusive.  The setValidity() method
776     is a friendlier way to set the validity value.
777
778     0 to 143 indicates 0 to 12 hours in 5 minute increments (0 = 5 minutes).
779     144 to 167 indicates 12 hrs 30 min to 24 hrs in 30 minute increments.
780     168 to 196 indicates 2 days to 30 days in 1 day increments.
781     197 to 255 indicates 5 weeks to 63 weeks in 1 week increments.
782
783     \sa gsmValidityPeriod(), validityPeriod()
784 */
785 void QSMSMessage::setGsmValidityPeriod(uint value)
786 {
787     dwrite()->setGsmValidityPeriod(value);
788 }
789
790 /*!
791     Returns the GSM validity period value, between 0 and 255, inclusive.
792
793     \sa setGsmValidityPeriod(), setValidityPeriod()
794 */
795 uint QSMSMessage::gsmValidityPeriod() const
796 {
797     return d->gsmValidityPeriod();
798 }
799
800 /*!
801     Sets the SMS message's \a timestamp.
802
803     \sa timestamp()
804 */
805 void QSMSMessage::setTimestamp(const QDateTime& timestamp)
806 {
807     dwrite()->mTimestamp = timestamp;
808 }
809
810 /*!
811     Returns the SMS message's timestamp, which will be null if the
812     message does not have a timestamp.
813
814     \sa setTimestamp()
815 */
816 QDateTime QSMSMessage::timestamp() const
817 {
818     return d->mTimestamp;
819 }
820
821 /*!
822     Returns the SMS message type.
823
824     \sa setMessageType()
825 */
826 QSMSMessage::MessageType QSMSMessage::messageType() const
827 {
828     return d->mMessageType;
829 }
830
831 /*!
832     Sets the SMS message type to \a m.  There is rarely any need to
833     set this to something other than QSMSMessage::Normal.
834
835     \sa messageType()
836 */
837 void QSMSMessage::setMessageType(MessageType m)
838 {
839     dwrite()->mMessageType  = m;
840 }
841
842 /*!
843     Sets the SMS message's user data headers to \a value.
844
845     \sa headers()
846 */
847 void QSMSMessage::setHeaders(const QByteArray& value)
848 {
849     dwrite()->mHeaders = value;
850 }
851
852 /*!
853     Returns the SMS message's user data headers.
854
855     \sa setHeaders()
856 */
857 const QByteArray& QSMSMessage::headers() const
858 {
859     return d->mHeaders;
860 }
861
862 /*!
863     Clear all body parts from this SMS message.
864
865     \sa addPart(), addParts(), parts()
866 */
867 void QSMSMessage::clearParts()
868 {
869     dwrite()->mParts.clear();
870     dwrite()->mCachedBody = QString();
871 }
872
873 /*!
874     Add a new body \a part to this SMS message.
875
876     \sa clearParts(), addParts(), parts()
877 */
878 void QSMSMessage::addPart( const QSMSMessagePart& part )
879 {
880     // We need a writable copy.
881     dwrite();
882
883     // Clear the part list if we have one empty text part.
884     if ( d->mParts.count() == 1 &&
885          d->mParts[0].isText() &&
886          d->mParts[0].text().length() == 0 ) {
887         d->mParts.clear();
888     }
889
890     // Append the new part and clear the cached text.
891     d->mParts.append( part );
892     d->mCachedBody = QString();
893 }
894
895 /*!
896     Add a list of body \a parts to this SMS message.
897
898     \sa clearParts(), addPart(), parts()
899 */
900 void QSMSMessage::addParts( const QList<QSMSMessagePart>& parts )
901 {
902     // We need a writable copy.
903     dwrite();
904
905     if ( d->mParts.count() == 1 &&
906          d->mParts[0].isText() &&
907          d->mParts[0].text().length() == 0 ) {
908         d->mParts.clear();
909     }
910     d->mParts += parts;
911     d->mCachedBody = QString();
912 }
913
914 /*!
915     Returns a list of all body parts in this SMS message.
916
917     \sa clearParts(), addParts(), addPart()
918 */
919 QList<QSMSMessagePart> QSMSMessage::parts() const
920 {
921     return d->mParts;
922 }
923
924 /*!
925     Compute an estimate for the number of messages that will need
926     to be used to send this SMS message (\a numMessages), and the
927     number of spare characters that are left in the last message
928     before it overflows (\a spaceLeftInLast).
929
930     This function may be useful in user interfaces to indicate to
931     the user that an SMS message needs to be sent in multiple pieces,
932     costing the user more money.
933 */
934 void QSMSMessage::computeSize( uint& numMessages, uint& spaceLeftInLast ) const
935 {
936     int part = findPart( "application/x-qtopia-wdp-ports" );
937     if ( part != -1 ) {
938         // This is an application datagram, which has its size
939         // computed using a different algorithm.
940         uint headerLen = 3 + d->mParts[(uint)part].data().size();
941         uint dataLen = applicationData().size();
942         if ( ( headerLen + dataLen ) <= 140 ) {
943             numMessages = 1;
944             spaceLeftInLast = 134 - headerLen - dataLen;
945         } else {
946             uint partSize = ( 134 - headerLen );
947             numMessages = ( dataLen + partSize - 1 ) / partSize;
948             spaceLeftInLast = dataLen - numMessages * partSize;
949         }
950         return;
951     }
952
953     QSMSDataCodingScheme scheme = bestScheme();
954     uint len;
955     QString body = text();
956
957     if ( scheme == QSMS_DefaultAlphabet ) {
958
959         // Encode the message using 7-bit GSM.
960         len = body.length();
961         if ( len <= 160 ) {
962             numMessages = 1;
963             spaceLeftInLast = 160 - len;
964         } else {
965             // 153 = 160 - fragment_header_size (7).
966             numMessages = ( len + 152 ) / 153;
967             len %= 153;
968             if ( len != 0 )
969                 spaceLeftInLast = 153 - len;
970             else
971                 spaceLeftInLast = 0;
972         }
973
974     } else if ( scheme == QSMS_8BitAlphabet ) {
975
976         // Encode the message using an 8-bit character set.
977         QByteArray converted = d->mCodec->fromUnicode( body );
978         len = converted.length();
979         if ( len <= 140 ) {
980             numMessages = 1;
981             spaceLeftInLast = 140 - len;
982         } else {
983             // 134 = 140 - fragment_header_size (6).
984             numMessages = ( len + 133 ) / 134;
985             len %= 134;
986             if ( len != 0 )
987                 spaceLeftInLast = 134 - len;
988             else
989                 spaceLeftInLast = 0;
990         }
991
992     } else {
993
994         // Encode the message with unicode.
995         len = body.length();
996         if ( len <= 70 ) {
997             numMessages = 1;
998             spaceLeftInLast = 70 - len;
999         } else {
1000             // 67 = 70 - fragment_header_size (3).
1001             numMessages = ( len + 66 ) / 67;
1002             len %= 67;
1003             if ( len != 0 )
1004                 spaceLeftInLast = 67 - len;
1005             else
1006                 spaceLeftInLast = 0;
1007         }
1008     }
1009 }
1010
1011 /*!
1012     Returns the destination port number if this SMS message contains
1013     an application datagram, or -1 if not an application datagram.
1014
1015     When an SMS message is received that has a destination port
1016     number, Qtopia will attempt to find a QDS service that can handle it.
1017     Qtopia first looks for a QDS service named \c push for the MIME type
1018     \c T from the WAP push header.  Next, it looks for a service named
1019     \c push for the MIME type \c{application/x-smsapp-N} where
1020     \c N is the port number in decimal.
1021
1022     If a matching service is found, then the SMS message is
1023     sent to the corresponding application via QDS.  The QCop
1024     message is that specified in the QDS service definition.
1025     Thus, applications can register to receive special SMS messages.
1026
1027     The following QDS definition, in \c{etc/qds/ContactsPhone} will
1028     direct vcard's that are received via WAP to the \c ContactsPhone
1029     service.  The \c ContactsPhone service is normally implemented
1030     by the \c addressbook program.
1031
1032     \code
1033     [Translation]
1034     File=QtopiaServices
1035     Context=ContactsPhone
1036     [pushVCard]
1037     RequestDataType=text/x-vcard
1038     ResponseDataType=
1039     Attributes="push"
1040     Description[]=Receive a vcard via WAP push
1041     \endcode
1042
1043     The \c Attributes must contain \c push and the \c RequestDataType
1044     must be the MIME type to be dispatched.  The QCop message that is
1045     delivered to the application will have the name
1046     \c pushVCard(QDSActionRequest).  The data in the action request
1047     will be the payload of the push message.
1048
1049     The auxilary data in the action request will be a QByteArray
1050     containing the full QSMSMessage object, from which the application
1051     can extra header information if it needs it.  Use the QSMSMessage
1052     datastream operators to extract the QSMSMessage object.
1053
1054     If the application fails to process an SMS message that is sent to
1055     it via QCop, then it will be lost.  It is important that such
1056     applications take steps to save the message if necessary.
1057
1058     If a matching service is not found, then the SMS message
1059     will be delivered to the \c qtmail application normally,
1060     and then saved by that application.
1061
1062     \sa setDestinationPort(), sourcePort()
1063 */
1064 int QSMSMessage::destinationPort() const
1065 {
1066     int part = findPart( "application/x-qtopia-wdp-ports" );
1067     if ( part == -1 )
1068         return -1;
1069
1070     QByteArray data = d->mParts[(uint)part].data();
1071     if ( data.size() == 4 ) {
1072
1073         return ((((int)(data[0])) & 0xFF) << 8) |
1074                 (((int)(data[1])) & 0xFF);
1075
1076     } else if ( data.size() == 2 ) {
1077
1078         return (((int)(data[0])) & 0xFF);
1079     }
1080
1081     return -1;
1082 }
1083
1084 /*!
1085     Sets the destination port number for an SMS message that contains
1086     an application datagram to \a value.
1087
1088     \sa destinationPort(), sourcePort()
1089 */
1090 void QSMSMessage::setDestinationPort(int value)
1091 {
1092     QByteArray data;
1093     int source;
1094     int part = findPart( "application/x-qtopia-wdp-ports" );
1095     if ( part == -1 ) {
1096         data.resize(4);
1097         data[0] = (char)(value >> 8);
1098         data[1] = (char)value;
1099         data[2] = (char)0;
1100         data[3] = (char)0;
1101     } else {
1102         source = sourcePort();
1103         data.resize(4);
1104         data[0] = (char)(value >> 8);
1105         data[1] = (char)value;
1106         data[2] = (char)(source >> 8);
1107         data[3] = (char)source;
1108     }
1109     removeParts( "application/x-qtopia-wdp-ports" );
1110     addPart( QSMSMessagePart( "application/x-qtopia-wdp-ports", data ) );
1111 }
1112
1113 /*!
1114     Returns the source port number if this SMS message contains
1115     an application datagram, or -1 if not an application datagram.
1116
1117     \sa setSourcePort(), destinationPort()
1118 */
1119 int QSMSMessage::sourcePort() const
1120 {
1121     int part = findPart( "application/x-qtopia-wdp-ports" );
1122     if ( part == -1 )
1123         return -1;
1124
1125     QByteArray data = d->mParts[(uint)part].data();
1126     if ( data.size() == 4 ) {
1127
1128         return ((((int)(data[2])) & 0xFF) << 8) |
1129                 (((int)(data[3])) & 0xFF);
1130
1131     } else if ( data.size() == 2 ) {
1132
1133         return (((int)(data[1])) & 0xFF);
1134     }
1135
1136     return -1;
1137 }
1138
1139 /*!
1140     Sets the source port number for an SMS message that contains
1141     an application datagram to \a value.
1142
1143     \sa sourcePort(), destinationPort()
1144 */
1145 void QSMSMessage::setSourcePort(int value)
1146 {
1147     QByteArray data;
1148     int dest;
1149     int part = findPart( "application/x-qtopia-wdp-ports" );
1150     if ( part == -1 ) {
1151         data.resize(4);
1152         data[0] = (char)0;
1153         data[1] = (char)0;
1154         data[2] = (char)(value >> 8);
1155         data[3] = (char)value;
1156     } else {
1157         dest = destinationPort();
1158         data.resize(4);
1159         data[0] = (char)(dest >> 8);
1160         data[1] = (char)dest;
1161         data[2] = (char)(value >> 8);
1162         data[3] = (char)value;
1163     }
1164     removeParts( "application/x-qtopia-wdp-ports" );
1165     addPart( QSMSMessagePart( "application/x-qtopia-wdp-ports", data ) );
1166 }
1167
1168 /*!
1169     Returns the data if this SMS message contains an application datagram.
1170     Returns an empty byte array if this message is not a datagram.
1171
1172     \sa setApplicationData()
1173 */
1174 QByteArray QSMSMessage::applicationData() const
1175 {
1176     QByteArray data;
1177     QList<QSMSMessagePart>::ConstIterator iter;
1178     uint size;
1179     for ( iter = d->mParts.begin(); iter != d->mParts.end(); ++iter ) {
1180         if ( (*iter).mimeType() == "application/x-qtopia-wdp" ) {
1181             size = data.size();
1182             data.resize( size + (*iter).data().size() );
1183             memcpy( data.data() + size, (*iter).data().data(),
1184                     (*iter).data().size() );
1185         }
1186     }
1187     return data;
1188 }
1189
1190 /*!
1191     Sets the data within an SMS message that contains an application
1192     datagram to \a value.
1193
1194     \sa applicationData()
1195 */
1196 void QSMSMessage::setApplicationData(const QByteArray& value)
1197 {
1198     removeParts( "application/x-qtopia-wdp" );
1199     addPart( QSMSMessagePart( "application/x-qtopia-wdp", value ) );
1200 }
1201
1202 /*!
1203     Sets the data coding scheme to use within an SMS message to \a value.
1204     If \a value is -1, then the system chooses the best data coding scheme
1205     based on the content.
1206
1207     This method is mainly of use with application datagrams, not text
1208     SMS messages.
1209
1210     \sa dataCodingScheme()
1211 */
1212 void QSMSMessage::setDataCodingScheme(int value)
1213 {
1214     dwrite()->mDataCodingScheme = value;
1215 }
1216
1217 /*!
1218     Returns the data coding scheme to use within an SMS message.
1219     If the value is -1, then the system chooses the best data coding scheme
1220     based on the content.
1221
1222     This method is mainly of use with application datagrams, not text
1223     SMS messages.
1224
1225     \sa setDataCodingScheme()
1226 */
1227 int QSMSMessage::dataCodingScheme() const
1228 {
1229     return d->mDataCodingScheme;
1230 }
1231
1232 /*!
1233     Sets the message class for this message to \a value.  The \value should
1234     be -1 if the system should choose a default message class.  The message
1235     class should otherwise be 0, 1, 2, or 3, according to 3GPP TS 03.38.
1236 */
1237 void QSMSMessage::setMessageClass(int value)
1238 {
1239     dwrite()->mMessageClass = value;
1240 }
1241
1242 /*!
1243     Get the message class for this message, or -1 if the system should
1244     choose a default message class.  The message class should otherwise
1245     be 0, 1, 2, or 3, according to 3GPP TS 03.38.
1246 */
1247 int QSMSMessage::messageClass() const
1248 {
1249     return d->mMessageClass;
1250 }
1251
1252 /*!
1253     Sets the SMS message's protocol field to \a value.
1254
1255     \sa protocol()
1256 */
1257 void QSMSMessage::setProtocol(int value)
1258 {
1259     dwrite()->mProtocol = value;
1260 }
1261
1262 /*!
1263     Returns the SMS message's protocol field.
1264
1265     \sa setProtocol()
1266 */
1267 int QSMSMessage::protocol() const
1268 {
1269     return d->mProtocol;
1270 }
1271
1272 /*!
1273     Returns true if this message needs to be split into multiple messages
1274     before being transmitted over a GSM network; otherwise returns false.
1275
1276     \sa split()
1277 */
1278 bool QSMSMessage::shouldSplit() const
1279 {
1280     uint numMessages, spaceLeftInLast;
1281     this->computeSize( numMessages, spaceLeftInLast );
1282     return ( numMessages <=1 ? false : true );
1283 }
1284
1285 /*!
1286     Split this message into several messages of smaller size for
1287     transmission over a GSM network.
1288
1289     \sa shouldSplit()
1290 */
1291 QList<QSMSMessage> QSMSMessage::split() const
1292 {
1293     QList<QSMSMessage> list;
1294     uint numMessages, spaceLeftInLast;
1295     static uint fragmentCounter =0;
1296
1297     computeSize( numMessages, spaceLeftInLast );
1298     if ( numMessages <= 1 ) {
1299         // Splitting is not necessary, so return a list with one message.
1300         list += *this;
1301         return list;
1302     }
1303
1304     // Get the number of characters to transmit in each fragment.
1305     int split;
1306     QSMSDataCodingScheme scheme = bestScheme();
1307     switch ( scheme ) {
1308         case QSMS_DefaultAlphabet:  split = 153; break;
1309         case QSMS_8BitAlphabet:     split = 134; break;
1310         default:                    split = 67; break;
1311     }
1312
1313     // Split the message to create sub-messages and transmit them.
1314     int posn = 0;
1315     int len;
1316     uint number;
1317     QSMSMessage tmp;
1318     number = 1;
1319     if ( destinationPort() == -1 ) {
1320         // Splitting a simple text message.
1321         QString txt = text();
1322         while ( posn < txt.length() ) {
1323             tmp = *this;
1324             len = txt.length() - posn;
1325             if ( len > split ) {
1326                 len = split;
1327             }
1328             tmp.setText( txt.mid( posn, len ) );
1329             tmp.setFragmentHeader( fragmentCounter, number++,
1330                                    numMessages, scheme );
1331             posn += len;
1332             list.append(tmp);
1333         }
1334     } else {
1335         // Splitting a datagram message.
1336         QByteArray data = applicationData();
1337         QByteArray part;
1338         uint partSize = 134 - 6;
1339         while ( posn < data.size() ) {
1340             tmp = *this;
1341             if ( ( posn + partSize ) <= (uint)data.size() ) {
1342                 part.resize(partSize);
1343                 memcpy(part.data(), data.data() + posn, partSize );
1344             } else {
1345                 part.resize(data.size() - posn);
1346                 memcpy(part.data(), data.data() + posn, data.size() - posn);
1347             }
1348             tmp.setDestinationPort( destinationPort() );// Force 16-bit ports.
1349             tmp.setFragmentHeader( fragmentCounter, number++,
1350                                    numMessages, QSMS_8BitAlphabet );
1351             tmp.setApplicationData( part );
1352             list.append(tmp);
1353             posn += partSize;
1354         }
1355     }
1356
1357     // Increase the fragment counter for the next multi-part SMS message.
1358     fragmentCounter = ( fragmentCounter + 1 ) & 0xFF;
1359
1360     return list;
1361 }
1362
1363 /*!
1364     Convert this SMS message into its binary PDU form, according to
1365     3GPP TS 03.40 and 3GPP TS 23.040.  If the message has a recipient,
1366     then a SUBMIT message will be constructed.  If the message does not
1367     have a recipient, then a DELIVER message will be constructed.
1368
1369     \sa fromPdu()
1370 */
1371 QByteArray QSMSMessage::toPdu() const
1372 {
1373     QSMSSubmitMessage submit( *this, recipient().isEmpty() );
1374     return submit.toByteArray();
1375 }
1376
1377 /*!
1378     Convert a binary \a pdu into an SMS message, according to
1379     3GPP TS 03.40 and 3GPP TS 23.040.
1380
1381     \sa toPdu()
1382 */
1383 QSMSMessage QSMSMessage::fromPdu( const QByteArray& pdu )
1384 {
1385     QSMSDeliverMessage pdumsg( pdu );
1386     return pdumsg.unpack();
1387 }
1388
1389 /*!
1390     Returns the length of the service center address on the start of \a pdu.
1391     This is typically used with AT-based GSM modems that need to transmit
1392     the length of the pdu, excluding the service center address,
1393     along with the \c{AT+CMGS} command.
1394 */
1395 int QSMSMessage::pduAddressLength( const QByteArray& pdu )
1396 {
1397     if( pdu.length() > 0 )
1398         return (pdu[0] & 0xFF) + 1;
1399     else
1400         return 0;
1401 }
1402
1403 int QSMSMessage::findPart( const QString& mimeType ) const
1404 {
1405     QList<QSMSMessagePart>::ConstIterator iter;
1406     int posn = 0;
1407     for ( iter = d->mParts.begin(); iter != d->mParts.end(); ++iter ) {
1408         if ( (*iter).mimeType() == mimeType )
1409             return posn;
1410         ++posn;
1411     }
1412     return -1;
1413 }
1414
1415 void QSMSMessage::removeParts( const QString& mimeType )
1416 {
1417     dwrite();
1418     QList<QSMSMessagePart>::Iterator iter;
1419     for ( iter = d->mParts.begin(); iter != d->mParts.end(); ) {
1420         if ( (*iter).mimeType() == mimeType ) {
1421             iter = d->mParts.erase( iter );
1422         } else {
1423             ++iter;
1424         }
1425     }
1426 }
1427
1428 void QSMSMessage::setFragmentHeader( uint refNum, uint part, uint numParts,
1429                                     QSMSDataCodingScheme scheme )
1430 {
1431     dwrite()->mBestScheme = scheme;
1432     uint len = d->mHeaders.size();
1433     d->mHeaders.resize( len + 5 );
1434     d->mHeaders[len++] = 0;         // Type for concatenated short messages.
1435     d->mHeaders[len++] = 3;         // Length of header information.
1436     d->mHeaders[len++] = (char)refNum;
1437     d->mHeaders[len++] = (char)numParts;
1438     d->mHeaders[len++] = (char)part;
1439 }
1440
1441
1442 void QSMSMessage::unpackHeaderParts()
1443 {
1444     QByteArray headers = dwrite()->mHeaders;
1445     uint posn = 0;
1446     uint tag, len;
1447     uint temp;
1448     QString type;
1449     while ( ( posn + 2 ) <= (uint)(headers.size()) ) {
1450         tag = (unsigned char)(headers[posn]);
1451         len = (unsigned char)(headers[posn + 1]);
1452         if ( ( posn + len + 2 ) > (uint)(headers.size()) )
1453             break;
1454         switch ( (SMSHeaderKind)tag ) {
1455
1456             case SMS_HK_Predefined_Sound:
1457             {
1458                 // Predefined sound type.
1459                 if ( len >= 2 ) {
1460                     QByteArray data( len - 1, 0 );
1461                     memcpy( data.data(), headers.data() + posn + 3, len - 1 );
1462                     addPart( QSMSMessagePart
1463                         ( "application/x-qtopia-predefined-sound", data,
1464                           headers[posn + 2] & 0xFF ) );
1465                 }
1466             }
1467             break;
1468
1469             case SMS_HK_User_Defined_Sound:
1470             {
1471                 // User defined sound type.
1472                 if ( len >= 2 ) {
1473                     QByteArray data( len - 1, 0 );
1474                     memcpy( data.data(), headers.data() + posn + 3, len - 1 );
1475                     addPart( QSMSMessagePart
1476                         ( "audio/imelody", data, headers[posn + 2] & 0xFF ) );
1477                 }
1478             }
1479             break;
1480
1481             case SMS_HK_Predefined_Animation:
1482             {
1483                 // Predefined animation type.
1484                 if ( len >= 2 ) {
1485                     QByteArray data( len - 1, 0 );
1486                     memcpy( data.data(), headers.data() + posn + 3, len - 1 );
1487                     addPart( QSMSMessagePart
1488                         ( "application/x-qtopia-predefined-animation", data,
1489                           headers[posn + 2] & 0xFF ) );
1490                 }
1491             }
1492             break;
1493
1494             case SMS_HK_Large_Animation:
1495             case SMS_HK_Large_Picture:
1496             {
1497                 // 32x32 monochrome animation or image - turn it into a WBMP.
1498                 if ( len >= 1 ) {
1499                     QByteArray data( len + 3, 0 );
1500                     data[0] = 0x00;
1501                     data[1] = 0x00;
1502                     data[2] = 0x20;
1503                     data[3] = 0x20;
1504                     for ( temp = 0; temp < (len - 1); ++temp ) {
1505                         data[4 + temp] = (char)(~(headers[posn + 3 + temp]));
1506                     }
1507                     addPart( QSMSMessagePart
1508                         ( "image/vnd.wap.wbmp", data,
1509                           headers[posn + 2] & 0xFF ) );
1510                 }
1511             }
1512             break;
1513
1514             case SMS_HK_Small_Animation:
1515             case SMS_HK_Small_Picture:
1516             {
1517                 // 16x16 monochrome animation or image - turn it into a WBMP.
1518                 if ( len >= 1 ) {
1519                     QByteArray data( len + 3, 0 );
1520                     data[0] = 0x00;
1521                     data[1] = 0x00;
1522                     data[2] = 0x10;
1523                     data[3] = 0x10;
1524                     for ( temp = 0; temp < (len - 1); ++temp ) {
1525                         data[4 + temp] = (char)(~(headers[posn + 3 + temp]));
1526                     }
1527                     addPart( QSMSMessagePart
1528                         ( "image/vnd.wap.wbmp", data,
1529                           headers[posn + 2] & 0xFF ) );
1530                 }
1531             }
1532             break;
1533
1534             case SMS_HK_Variable_Picture:
1535             {
1536                 // Variable-sized monochrome image - turn it into a WBMP.
1537                 if ( len >= 3 ) {
1538                     QByteArray data( len - 1, 0 );
1539                     data[0] = 0x00;
1540                     data[1] = 0x00;
1541                     data[2] = headers[posn + 3];
1542                     data[3] = headers[posn + 4];
1543                     for ( temp = 2; temp < (len - 1); ++temp ) {
1544                         data[2 + temp] = (char)(~(headers[posn + 3 + temp]));
1545                     }
1546                     addPart( QSMSMessagePart
1547                         ( "image/vnd.wap.wbmp", data,
1548                           headers[posn + 2] & 0xFF ) );
1549                 }
1550             }
1551             break;
1552
1553             case SMS_HK_Concat_8Bit:    break;
1554             case SMS_HK_Concat_16Bit:   break;
1555
1556             case SMS_HK_AppPort_8Bit:
1557             case SMS_HK_AppPort_16Bit:
1558             {
1559                 QByteArray data( len, 0 );
1560                 memcpy( data.data(), headers.data() + posn + 2, len );
1561                 addPart( QSMSMessagePart
1562                     ( "application/x-qtopia-wdp-ports", data ) );
1563             }
1564             break;
1565
1566             default:
1567             {
1568                 // Add the unknown part as "application/x-qtopia-sms-N".
1569                 // Maybe qtmail will know what to do with it.
1570                 QByteArray data( len, 0 );
1571                 memcpy( data.data(), headers.data() + posn + 2, len );
1572                 addPart( QSMSMessagePart( "application/x-qtopia-sms-" +
1573                                          QString::number( tag ), data ) );
1574             }
1575             break;
1576         }
1577         posn += 2 + len;
1578     }
1579 }
1580
1581 /*!
1582     \internal
1583     \fn void QSMSMessage::deserialize(Stream &stream)
1584 */
1585 template <typename Stream> void QSMSMessage::deserialize(Stream &stream)
1586 {
1587     dwrite()->readFromStream( stream );
1588 }
1589
1590 /*!
1591     \internal
1592     \fn void QSMSMessage::serialize(Stream &stream) const
1593 */
1594 template <typename Stream> void QSMSMessage::serialize(Stream &stream) const
1595 {
1596     d->writeToStream( stream );
1597 }
1598
1599 QPDUMessage::QPDUMessage()
1600 {
1601     mPosn = 0;
1602     mBits = 0;
1603 }
1604
1605 QPDUMessage::QPDUMessage(const QByteArray &data)
1606 {
1607     mBuffer = data;
1608     mPosn = 0;
1609     mBits = 0;
1610 }
1611
1612 QPDUMessage::~QPDUMessage()
1613 {
1614 }
1615
1616 QPDUMessage::QPDUMessage(const QPDUMessage &msg)
1617 {
1618     mBuffer = msg.mBuffer;
1619     mPosn = msg.mPosn;
1620     mBits = msg.mBits;
1621 }
1622
1623 void QPDUMessage::skipOctet()
1624 {
1625     if ( mPosn < mBuffer.size() )
1626         ++mPosn;
1627 }
1628
1629 QByteArray QPDUMessage::getOctets( uint len )
1630 {
1631     QByteArray result;
1632     if ( ( mBuffer.size() - mPosn ) < (int)len ) {
1633         result = mBuffer.mid( mPosn );
1634         abort();
1635     } else {
1636         result = mBuffer.mid( mPosn, (int)len );
1637         mPosn += len;
1638     }
1639     return result;
1640 }
1641
1642 void QPDUMessage::setBit(int b, bool on)
1643 {
1644     if ( on )
1645         mBits |= (char)(Unit << b);
1646     else
1647         mBits &= (char)(~(Unit << b));
1648 }
1649
1650 void QPDUMessage::setBits(int offset, int len, int val)
1651 {
1652     uint mask = ((Unit << len) - 1) << offset;
1653     mBits = (char)((mBits & ~mask) | ((val << offset) & mask));
1654 }
1655
1656 void QPDUMessage::commitBits()
1657 {
1658     mBuffer += mBits;
1659     mBits = 0;
1660 }
1661
1662 bool QPDUMessage::bit(int b)
1663 {
1664     if ( needOctets(1) )
1665         return (mBuffer[mPosn] & (Unit << b));
1666     else
1667         return 0;
1668 }
1669
1670 unsigned char QPDUMessage::bits(int offset, int len)
1671 {
1672     if ( needOctets(1) )
1673         return (unsigned char)
1674             ((mBuffer[mPosn] >> offset) & ((Unit << len) - 1));
1675     else
1676         return 0;
1677 }
1678
1679 unsigned char QPDUMessage::getOctet()
1680 {
1681     if ( needOctets(1) )
1682         return (unsigned char)(mBuffer[mPosn++]);
1683     else
1684         return 0;
1685 }
1686
1687 unsigned char QPDUMessage::peekOctet() const
1688 {
1689     if ( needOctets(1) )
1690         return (unsigned char)(mBuffer[mPosn]);
1691     else
1692         return 0;
1693 }
1694
1695 // Collapse 8-bit-aligned GSM data to its 7-bit form.
1696 static QByteArray collapse7Bit( const QByteArray& in )
1697 {
1698     QByteArray out;
1699     int byte = 0;
1700     int size = 0;
1701     for ( int posn = 0; posn < in.length(); ++posn ) {
1702         for ( int bit = 0; bit < 7; ++bit ) {
1703             if ( ( in[posn] & (1 << bit) ) != 0 )
1704                 byte |= (1 << size);
1705             ++size;
1706             if ( size >= 8 ) {
1707                 out += (char)byte;
1708                 byte = 0;
1709                 size = 0;
1710             }
1711         }
1712     }
1713     if ( size != 0 ) {
1714         out += (char)byte;
1715     }
1716     return out;
1717 }
1718
1719 void QPDUMessage::setAddress(const QString &strin, bool SCAddress)
1720 {
1721     SMSAddressType at;
1722     int len, digit, octet;
1723     int posn;
1724     const int maxPhoneNumberLen = 15;
1725     QString str( strin );
1726     str.truncate( maxPhoneNumberLen );
1727
1728     // Determine the address type and length.
1729     at = SMS_Address_Unknown;
1730     len = 0;
1731     for ( posn = 0; posn < str.length(); ++posn ) {
1732         switch ( str[posn].unicode() ) {
1733             case '+':
1734                 at = SMS_Address_International;
1735                 break;
1736
1737             case '0': case '1': case '2': case '3':
1738             case '4': case '5': case '6': case '7':
1739             case '8': case '9': case '*': case '#':
1740             case 'A': case 'B': case 'C': case 'a':
1741             case 'b': case 'c':
1742                 ++len;
1743                 break;
1744
1745             default:
1746                 // Probably an alpha-numeric address.
1747                 ++len;
1748                 at = SMS_Address_AlphaNumeric;
1749                 break;
1750         }
1751     }
1752     if ( SCAddress )
1753         len = (len + 1) / 2;
1754
1755     // Bail out early if the address is zero-length.
1756     if ( !len ) {
1757         appendOctet(0);
1758         return;
1759     }
1760
1761     // Handle alphanumeric address fields.
1762     if ( at == SMS_Address_AlphaNumeric ) {
1763         // Convert the address and calculate its encoded length.
1764         QTextCodec *codec = QAtUtils::codec( "gsm-noloss" );
1765         QByteArray bytes = collapse7Bit( codec->fromUnicode( strin ) );
1766         len = bytes.size();
1767
1768         // Need an extra byte for SCAddress fields.
1769         if ( SCAddress )
1770             len++;
1771
1772         // Output the length of the encoded address.
1773         appendOctet(len);
1774
1775         // Output the type of number information.
1776         setBits(0, 4, SMS_NumberId_Unknown);
1777         setBits(4, 3, at);
1778         setBit(7, true);
1779         commitBits();
1780
1781         // Output the encoded address and exit.
1782         mBuffer += bytes;
1783         return;
1784     }
1785
1786     // SCAddress len = octets + type specifier
1787     if ( SCAddress )
1788         len++;
1789
1790     appendOctet(len);
1791
1792     setBits(0, 4, SMS_Phone);
1793     setBits(4, 3, at);
1794     setBit(7, true);
1795     commitBits();
1796
1797     bool upper4 = false;
1798     octet = 0;
1799     for ( posn = 0; posn < str.length(); posn++ ) {
1800         switch ( str[posn].unicode() ) {
1801             case '0':           digit = 0; break;
1802             case '1':           digit = 1; break;
1803             case '2':           digit = 2; break;
1804             case '3':           digit = 3; break;
1805             case '4':           digit = 4; break;
1806             case '5':           digit = 5; break;
1807             case '6':           digit = 6; break;
1808             case '7':           digit = 7; break;
1809             case '8':           digit = 8; break;
1810             case '9':           digit = 9; break;
1811             case '*':           digit = 10; break;
1812             case '#':           digit = 11; break;
1813             case 'A': case 'a': digit = 12; break;
1814             case 'B': case 'b': digit = 13; break;
1815             case 'C': case 'c': digit = 14; break;
1816             default:            digit = -1; break;
1817         }
1818         if ( digit != -1 ) {
1819             if ( !upper4 ) {
1820                 octet = digit;
1821             } else {
1822                 octet |= (digit << 4);
1823                 appendOctet( octet );
1824             }
1825             upper4 = !upper4;
1826         }
1827     }
1828     if ( upper4 ) {
1829         appendOctet( octet | 0xF0 );
1830     }
1831 }
1832
1833 // Expand the 7-bit GSM data to 8-bit-aligned characters.
1834 static QByteArray expand7Bit( const QByteArray& in )
1835 {
1836     QByteArray out;
1837     int byte = 0;
1838     int size = 0;
1839     for ( int posn = 0; posn < in.length(); ++posn ) {
1840         for ( int bit = 0; bit < 8; ++bit ) {
1841             if ( ( in[posn] & (1 << bit) ) != 0 )
1842                 byte |= (1 << size);
1843             ++size;
1844             if ( size >= 7 ) {
1845                 out += (char)byte;
1846                 byte = 0;
1847                 size = 0;
1848             }
1849         }
1850     }
1851     return out;
1852 }
1853
1854 QString QPDUMessage::address(bool SCAddress)
1855 {
1856     QString str = "";
1857
1858     // Get the address length and validate it.
1859     if ( !needOctets(1) )
1860         return str;
1861     uint len = (((uint)(getOctet())) & 0xFF);
1862     if ( !needOctets(len) ) {
1863         abort();
1864         return str;
1865     }
1866
1867     if ( len ) {
1868         SMSAddressType at = (SMSAddressType) bits(4, 3);
1869
1870         if ( at == SMS_Address_International )
1871             str += "+";
1872
1873         skipOctet();
1874         if ( !SCAddress ) {
1875             if ( at != SMS_Address_AlphaNumeric ) {
1876                 len = len / 2 + (len % 2);
1877             }
1878         } else {
1879             len--;
1880         }
1881
1882         if ( at != SMS_Address_AlphaNumeric ) {
1883             unsigned char c;
1884             for (int i = 0; i < (int)len; i++) {
1885                 c = peekOctet() & 0x0F;
1886                 str += (char) ('0' + c);
1887                 c = (peekOctet() & 0xF0) >> 4;
1888                 if ( c != 0xf )
1889                     str += (char) ('0' + c);
1890                 skipOctet();
1891             }
1892         } else {
1893             // Recognize an alphanumeric address in the 7-bit GSM encoding.
1894             QTextCodec *codec = QAtUtils::codec( "gsm" );
1895             QByteArray bytes = expand7Bit( getOctets( len ) );
1896             str += codec->toUnicode( bytes );
1897         }
1898     }
1899
1900     return str;
1901 }
1902
1903 // Return the length of the service centre address.
1904 uint QPDUMessage::addressLength() const
1905 {
1906     if( mPosn < mBuffer.size() )
1907         return (((uint)peekOctet()) & 0xFF) + 1;
1908     else
1909         return 0;
1910 }
1911
1912 void QPDUMessage::setTimeStamp(const QDateTime &dt)
1913 {
1914     QDate date = dt.date();
1915     int tArr[6];
1916     tArr[0] = date.year();
1917     tArr[1] = date.month();
1918     tArr[2] = date.day();
1919
1920     QTime t = dt.time();
1921     tArr[3] = t.hour();
1922     tArr[4] = t.minute();
1923     tArr[5] = t.second();
1924
1925     for ( int i = 0; i < 6; i++) {
1926         appendOctet((unsigned char) ( (((tArr[i]/10)%10) & 0x0F) | (((tArr[i]%10) & 0x0F)<<4) ) );
1927     }
1928
1929     appendOctet(0x04); //arbitrary random timezone
1930 }
1931
1932 QDateTime QPDUMessage::timeStamp()
1933 {
1934     QDateTime d;
1935     unsigned char c, c4, date[7];
1936
1937     if ( !needOctets(7) ) {
1938         abort();
1939         return d;
1940     }
1941     for (int i = 0; i < 7; i++) {
1942         c = (peekOctet() & 0x0F);
1943         c4 = (peekOctet() & 0xF0) >> 4;
1944         date[i] = c*10 + c4;
1945         skipOctet();
1946     }
1947
1948     if ( date[0] < 80 ) {
1949         d.setDate( QDate(2000 + date[0], date[1], date[2]) );
1950     } else {
1951         d.setDate( QDate(1900 + date[0], date[1], date[2]) );
1952     }
1953     d.setTime( QTime(date[3], date[4], date[5]) );
1954
1955     return d;
1956 }
1957
1958 // Get the length of a string when encoded in the GSM 7-bit alphabet.
1959 static uint getEncodedLength( const QString& txt, uint size )
1960 {
1961     uint len = 0;
1962     for ( int u = 0; u < (int)size; u++ ) {
1963         if ( QGsmCodec::twoByteFromUnicode( txt[u].unicode() ) >= 256 )
1964             len += 2;
1965         else
1966             ++len;
1967     }
1968     return len;
1969 }
1970
1971 void QPDUMessage::setUserData(const QString &txt, QSMSDataCodingScheme scheme, QTextCodec *codec, const QByteArray& headers, bool implicitLength)
1972 {
1973     uint len = txt.length();
1974     uint u;
1975     uint encodedLen;
1976     uint headerLen = (uint)(headers.size());
1977     if ( headerLen )
1978         ++headerLen;
1979
1980     // Strip off everything except the alphabet bits.
1981     scheme = (QSMSDataCodingScheme)(scheme & 0x0C);
1982
1983     if ( scheme == QSMS_DefaultAlphabet ) {
1984
1985         // Encode the text using the 7-bit GSM alphabet.
1986         if ( len > 160 )
1987             len = 160;
1988         encodedLen = getEncodedLength( txt, len );
1989         while ( encodedLen > 160 ) {
1990             // Chop off some more characters until it is <= 160.
1991             --len;
1992             encodedLen = getEncodedLength( txt, len );
1993         }
1994         if (!implicitLength)
1995             appendOctet( encodedLen + ( headerLen * 8 + 6 ) / 7 );
1996         int bitCount = 0;
1997         unsigned short c;
1998         if ( headerLen > 0 ) {
1999             // Output the header and align on a septet boundary.
2000             bitCount = headerLen * 8;
2001             if ((bitCount % 7) != 0)
2002                 bitCount = 7 - (bitCount % 7);
2003             else
2004                 bitCount = 0;
2005             appendOctet( headerLen - 1 );
2006             for ( u = 0; u < headerLen - 1; u++ ) {
2007                 appendOctet( headers[u] );
2008             }
2009         }
2010         for ( u = 0; u < len; u++ ) {
2011             c = QGsmCodec::twoByteFromUnicode( txt[u].unicode() );
2012             if ( c >= 256 ) {
2013                 // Encode a two-byte sequence.
2014                 for ( int i = 0; i < 7; i++ ) {
2015                     if ( bitCount == 8 ) {
2016                         bitCount = 0;
2017                         commitBits();
2018                     }
2019                     setBit( bitCount++, (Unit << (i+8)) & c );
2020                 }
2021             }
2022             for ( int i = 0; i < 7; i++ ) {
2023                 if ( bitCount == 8 ) {
2024                     bitCount = 0;
2025                     commitBits();
2026                 }
2027                 setBit( bitCount++, (Unit << i) & c );
2028             }
2029         }
2030         if ( bitCount != 0 ) {
2031             commitBits();
2032         }
2033
2034     } else if ( scheme == QSMS_8BitAlphabet ) {
2035         // Encode the text using the codec's 8-bit alphabet.
2036         if ( !codec )
2037             codec = QAtUtils::codec( "iso-8859-1" );
2038         QByteArray converted = codec->fromUnicode( txt );
2039         len = converted.length();
2040         if ( len > 140 )
2041             len = 140;
2042         if (!implicitLength)
2043             appendOctet( len + headerLen );
2044         if ( headerLen > 0 ) {
2045             appendOctet( headerLen - 1 );
2046             for ( u = 0; u < headerLen - 1; u++ ) {
2047                 appendOctet( headers[u] );
2048             }
2049         }
2050         const char *s = (const char *)converted;
2051         for ( u = 0; u < len; u++ ) {
2052             appendOctet( (unsigned char)(s[u]) );
2053         }
2054
2055     } else {
2056
2057         // Encode the text using the 16-bit UCS2 alphabet.
2058         if ( len > 70 )
2059             len = 70;
2060         if (!implicitLength)
2061             appendOctet( len * 2 + headerLen );
2062         if ( headerLen > 0 ) {
2063             appendOctet( headerLen - 1 );
2064             for ( u = 0; u < headerLen - 1; u++ ) {
2065                 appendOctet( headers[u] );
2066             }
2067         }
2068         for ( u = 0; u < len; u++ ) {
2069             appendOctet( (unsigned char)(txt[u].unicode() >> 8) );
2070             appendOctet( (unsigned char)(txt[u].unicode() & 0xFF) );
2071         }
2072
2073     }
2074 }
2075
2076 // Determine if the headers in an SMS frame indicate that it
2077 // is an application datagram destinated for a particular port.
2078 static bool isSMSDatagram( const QByteArray& headers )
2079 {
2080     uint posn = 0;
2081     uint tag, len;
2082     while ( ( posn + 2 ) <= (uint)(headers.size()) ) {
2083         tag = (unsigned char)(headers[posn]);
2084         len = (unsigned char)(headers[posn + 1]);
2085         if ( ( posn + len + 2 ) > (uint)(headers.size()) )
2086             break;
2087         if ( tag == (uint)SMS_HK_AppPort_8Bit ||
2088              tag == (uint)SMS_HK_AppPort_16Bit ) {
2089             return true;
2090         }
2091         posn += len + 2;
2092     }
2093     return false;
2094 }
2095
2096 QString QPDUMessage::userData(QSMSDataCodingScheme scheme, QTextCodec *codec, QByteArray *& headers, bool hasHeaders, bool implicitLength)
2097 {
2098     QString str = "";
2099     uint len, headerLen;
2100     uint u;
2101     uint ch;
2102
2103     // Reset the header return.
2104     headers = 0;
2105
2106     // Get the length of the user data payload.
2107     if ( implicitLength ) {
2108         len = mBuffer.size() - mPosn;
2109     } else {
2110         if ( !needOctets(1) )
2111             return str;
2112         len = (uint)getOctet();
2113     }
2114
2115     // Strip off everything except the alphabet bits.
2116     scheme = (QSMSDataCodingScheme)(scheme & 0x0C);
2117
2118     if ( scheme == QSMS_DefaultAlphabet ) {
2119
2120         // Process a sequence in the default 7-bit GSM character set.
2121         int bitCount = 0;
2122         int startBit = 0;
2123         ch = 0;
2124         if ( implicitLength )
2125             len = len * 8 / 7;      // Convert 8-bit bytes to 7-bit characters.
2126         if ( hasHeaders ) {
2127             u = ( len * 7 + 7 ) / 8;
2128             if ( !u || !needOctets(u) )
2129                 return str;
2130             headerLen = getOctet();
2131             if ( headerLen >= u )
2132                 return str;
2133             headers = new QByteArray( getOctets( headerLen ) );
2134             if ( isSMSDatagram( *headers ) )
2135                 return str;
2136             u = ((headerLen + 1) * 8 + 6) / 7;
2137             len -= u;
2138             startBit = (headerLen + 1) * 8;
2139             if ((startBit % 7) != 0)
2140                 startBit = 7 - (startBit % 7);
2141             else
2142                 startBit = 0;
2143         }
2144         bool prefixed = false;
2145         while ( len > 0 ) {
2146             if ( !needOctets(1) )
2147                 return str;
2148             for ( int i = startBit; len > 0 && i < 8; i++ ) {
2149                 // test the bit and shift it down again, as i doesn't mark
2150                 // where we are in the current char, but bitCount does
2151                 ch |= ( ( peekOctet() & (Unit << i) ) >> i) << bitCount;
2152                 bitCount++;
2153                 if ( bitCount == 7 ) {
2154                     bitCount = 0;
2155                     if ( ch == 0x1B ) {     // Start of a two-byte encoding.
2156                         prefixed = true;
2157                     } else if ( prefixed ) {
2158                         str += QChar
2159                             ( QGsmCodec::twoByteToUnicode( 0x1B00 | ch ) );
2160                         prefixed = false;
2161                     } else {
2162                         str += QGsmCodec::singleToUnicode( (unsigned char)ch );
2163                     }
2164                     ch = 0;
2165                     --len;
2166                 }
2167             }
2168             startBit = 0;
2169             skipOctet();
2170         }
2171
2172     } else if ( scheme == QSMS_8BitAlphabet && codec ) {
2173
2174         // Process an 8-bit sequence using the supplied codec.
2175         if ( !needOctets(len) ) {
2176             abort();
2177             return str;
2178         }
2179         if ( hasHeaders ) {
2180             if ( !len || len < (uint)( (peekOctet() & 0xFF) + 1 ) ) {
2181                 abort();
2182                 return str;
2183             }
2184             headerLen = getOctet();
2185             headers = new QByteArray( getOctets( headerLen ) );
2186             if ( isSMSDatagram( *headers ) )
2187                 return str;
2188             len -= headerLen + 1;
2189         }
2190         str = codec->toUnicode( getOctets( len ) );
2191
2192     } else if ( scheme == QSMS_UCS2Alphabet ) {
2193
2194         // Process a UCS2 sequence.
2195         if ( !needOctets(len) ) {
2196             abort();
2197             return str;
2198         }
2199         if ( hasHeaders ) {
2200             if ( !len || len < (uint)( (peekOctet() & 0xFF) + 1 ) ) {
2201                 abort();
2202                 return str;
2203             }
2204             headerLen = getOctet();
2205             headers = new QByteArray( getOctets( headerLen ) );
2206             len -= headerLen + 1;
2207         }
2208         len /= 2;
2209         for ( u = 0; u < len; ++u ) {
2210             ch = (((uint)(getOctet() & 0xFF)) << 8);
2211             ch |= ((uint)(getOctet() & 0xFF));
2212             str += (QChar)ch;
2213         }
2214
2215     } else {
2216
2217         // Assume 8-bit for any other coding scheme value.
2218
2219         // Process an 8-bit sequence using the default Latin1 codec.
2220         if ( len > (uint)(mBuffer.size() - mPosn) ) {
2221             // The length field is invalid - use the actual size.
2222             len = mBuffer.size() - mPosn;
2223         }
2224         if ( hasHeaders ) {
2225             if ( !len || len < (uint)( (peekOctet() & 0xFF) + 1 ) ) {
2226                 abort();
2227                 return str;
2228             }
2229             headerLen = getOctet();
2230             headers = new QByteArray( getOctets( headerLen ) );
2231             if ( isSMSDatagram( *headers ) )
2232                 return str;
2233             len -= headerLen + 1;
2234         }
2235         for ( u = 0; u < len; ++u ) {
2236             str += (QChar)(getOctet() & 0xFF);
2237         }
2238
2239     }
2240
2241     return str;
2242 }
2243
2244 /*  Note that the meaning of messageType is also dependant on
2245     the direction of the message (orig. location) */
2246 SMSMessageType QPDUMessage::messageType()
2247 {
2248     if ( mBuffer.size() >= 1 &&
2249          mBuffer.size() >= ((mBuffer[0] & 0xFF) + 2) ) {
2250
2251         const char *ptr = mBuffer.constData();
2252         ptr += (*ptr & 0xFF) + 1;
2253         unsigned char c = *ptr & 3;
2254         return (SMSMessageType) c;
2255
2256     }
2257     return (SMSMessageType)0;
2258 }
2259
2260 QSMSSubmitMessage::QSMSSubmitMessage(const QSMSMessage &m, bool isDeliver)
2261     : QPDUMessage()
2262 {
2263     // Clear the pdu before we start.
2264     mBuffer = QByteArray();
2265     mPosn = 0;
2266     mBits = 0;
2267
2268     setAddress( m.serviceCenter(), true );
2269
2270     // If there is port and application data information, then
2271     // create header parts for them.  This is for sending datagram
2272     // based messages.
2273     QByteArray headers = m.headers();
2274     QSMSDataCodingScheme scheme = m.bestScheme();
2275     int part = m.findPart( "application/x-qtopia-wdp-ports" );
2276     if ( part != -1 ) {
2277         QByteArray portData = m.parts()[(uint)part].data();
2278         uint size = headers.size();
2279         headers.resize(size + portData.size() + 2);
2280         if ( portData.size() == 4 ) {
2281             headers[size++] = (char)SMS_HK_AppPort_16Bit;
2282         } else {
2283             headers[size++] = (char)SMS_HK_AppPort_8Bit;
2284         }
2285         headers[size++] = (char)(portData.size());
2286         memcpy(headers.data() + size, portData.data(), portData.size());
2287         int dataScheme = m.dataCodingScheme(); // User-supplied override.
2288         if ( dataScheme == -1 )
2289             dataScheme = 0xF5;      // Special value for datagrams.
2290         scheme = (QSMSDataCodingScheme)dataScheme;
2291     } else if ( m.messageClass() != -1 ) {
2292         scheme = (QSMSDataCodingScheme)
2293                     (scheme | QSMS_MessageClass | m.messageClass());
2294         int dataScheme = m.dataCodingScheme(); // User-supplied override.
2295         if ( dataScheme != -1 )
2296             scheme = (QSMSDataCodingScheme)dataScheme;
2297     } else {
2298         int dataScheme = m.dataCodingScheme(); // User-supplied override.
2299         if ( dataScheme != -1 )
2300             scheme = (QSMSDataCodingScheme)dataScheme;
2301     }
2302
2303     if ( !isDeliver )
2304         setBits(0, 2, SMS_Submit);
2305     else
2306         setBits(0, 2, SMS_Deliver);
2307
2308     setBit(2, false);                   // TP-Reject-Duplicates
2309     if ( !isDeliver && m.validityPeriod() == (uint)(-1) )
2310         setBits(3, 2, SMS_VF_NoPresent);
2311     else
2312         setBits(3, 2, SMS_VF_Relative);
2313     setBit(5, m.statusReportRequested());// TP-Status-Report-Requested;
2314     setBit(6, headers.size() != 0);     // TP-User-Data Header
2315     setBit(7, m.replyRequest());        // TP-Reply-Path
2316
2317     commitBits();       // first octet done
2318
2319     //second octet TP-MR (Message reference
2320     if( !isDeliver )
2321         appendOctet(0);
2322
2323     if ( !isDeliver ) {
2324         //third octet TP-DA (Destination Address)
2325         //len must be done later
2326         setAddress( m.recipient(), false );
2327     } else {
2328         setAddress( m.sender(), false);
2329     }
2330
2331
2332     //nth octet, TP-PID (protocol identifier)
2333     if ( !isDeliver )
2334         appendOctet(m.protocol());
2335     else
2336         appendOctet(1); //arbitrary protocol for deliver
2337
2338     // TP-DCS ( Data coding scheme )
2339     appendOctet(scheme);
2340
2341     if ( !isDeliver ) {
2342         // TP-VP ( Validity Period )
2343         if ( m.validityPeriod() != (uint)(-1) )
2344             appendOctet(m.gsmValidityPeriod());
2345     }  else {
2346         setTimeStamp(m.timestamp());
2347     }
2348
2349     // Set the user data field.
2350     if ( part == -1 ) {
2351         setUserData(m.text(), scheme, m.textCodec(), headers);
2352     } else {
2353         // The applicationData() is the text to be sent in the datagram.
2354         QByteArray appData = m.applicationData();
2355         uint len = appData.size();
2356         if ( ( len + headers.size() + 1 ) > 140 )
2357             len = 140 - headers.size() - 1;
2358         appendOctet( len + headers.size() + 1 );
2359         appendOctet( headers.size() );
2360         uint u;
2361         for ( u = 0; u < (uint)headers.size(); u++ ) {
2362             appendOctet( headers[u] );
2363         }
2364         for ( u = 0; u < len; u++ ) {
2365             appendOctet( appData[u] );
2366         }
2367     }
2368 }
2369
2370 QSMSDeliverMessage::QSMSDeliverMessage(const QByteArray &pdu)
2371     : QPDUMessage(pdu)
2372 {
2373 }
2374
2375 // Unpack a message that has a "//SCKL" header in its text body.
2376 // This is a standard to interoperate with networks and phones
2377 // that do not support user data headers properly.
2378 static void unpackSckl( QSMSMessage& m, const QString& text )
2379 {
2380     // Split into header and body, separated by a space.
2381     int index = text.indexOf( QChar(' ') );
2382     if ( index == -1 ) {
2383         m.addPart( QSMSMessagePart( text ) );
2384         return;
2385     }
2386     QString head = text.mid( 6, index - 6 );
2387     QString body = text.mid( index + 1 );
2388
2389     // Convert the header from hex into raw binary and then
2390     // copy its details into the real message header fields.
2391     QByteArray header = QAtUtils::fromHex( head );
2392     int len;
2393     if ( header.size() < 4 ) {
2394         len = header.size();
2395     } else {
2396         len = 4;
2397     }
2398     QByteArray ports( len, '\0' );
2399     memcpy( ports.data(), header.data(), len );
2400     m.addPart( QSMSMessagePart( "application/x-qtopia-wdp-ports", ports ) );
2401     if ( header.size() > 4 ) {
2402         QByteArray fragments( header.size() - 4 + 2, '\0' );
2403         fragments[0] = (char)( SMS_HK_Concat_8Bit );
2404         fragments[1] = (char)( header.size() - 4 );
2405         memcpy( fragments.data() + 2, header.data() + 4, header.size() - 4 );
2406         m.setHeaders( fragments );
2407     }
2408
2409     // Is the body text or binary?
2410     int port = m.destinationPort();
2411     if ( port == 226 || port == 9204 ||     // vCard port numbers.
2412          port == 228 || port == 9205 ) {    // vCalendar port numbers.
2413         m.setApplicationData( body.toLatin1() );
2414     } else {
2415         m.setApplicationData( QAtUtils::fromHex( body ) );
2416     }
2417 }
2418
2419 QSMSMessage QSMSDeliverMessage::unpack(QTextCodec *codec)
2420 {
2421     QSMSMessage m;
2422     bool moreMessages;
2423     bool statusReport;
2424     bool userDataHeader;
2425     bool replyPath;
2426     bool rejectDuplicates;
2427     unsigned char protocol;
2428     unsigned char scheme;
2429     uint msgType;
2430     uint validityFormat;
2431
2432     // Start from the beginning of the PDU.
2433     reset();
2434
2435     // Extract the service center address.
2436     m.setServiceCenter( address(true) );
2437
2438     // Pull apart the message header.  We handle both deliver and
2439     // submit messages, because we may have pulled a submit out
2440     // of the phone's outgoing SMS queue.
2441     if ( !needOctets(1) )
2442         return m;
2443     msgType = bits(0, 2);
2444     if ( msgType == SMS_Deliver ) {
2445         moreMessages = bit(2);
2446         // Bits 3 and 4 are unused for deliver messages.
2447         statusReport = bit(5);
2448         userDataHeader = bit(6);
2449         replyPath = bit(7);
2450         rejectDuplicates = false;
2451         validityFormat = SMS_VF_NoPresent;
2452     } else if ( msgType == SMS_Submit ) {
2453         moreMessages = false;
2454         rejectDuplicates = bit(2);
2455         validityFormat = bits(3, 2);
2456         statusReport = bit(5);
2457         userDataHeader = bit(6);
2458         replyPath = bit(7);
2459     } else {
2460         // Probably a delivery report, which we don't process.
2461         return m;
2462     }
2463     skipOctet();
2464
2465     // Remember the status and reply bits.
2466     m.setStatusReportRequested(statusReport);
2467     m.setReplyRequest(replyPath);
2468
2469     // Skip the message reference (submit messages only).
2470     if ( msgType == SMS_Submit ) {
2471         if ( !needOctets(1) )
2472             return m;
2473         skipOctet();
2474     }
2475
2476     // Get the address of the sender (delivery) or recipient (submit).
2477     if ( msgType == SMS_Deliver ) {
2478         m.setSender( address(false) );
2479     } else {
2480         m.setRecipient( address(false) );
2481     }
2482
2483     // Get the protocol identifier and data coding scheme.
2484     if ( !needOctets(2) )
2485         return m;
2486     protocol = getOctet();
2487     scheme = getOctet();
2488     m.setProtocol( protocol );
2489     m.setDataCodingScheme( scheme );
2490     if ( ( scheme & QSMS_MessageClass ) != 0 )
2491         m.setMessageClass( scheme & 0x03 );
2492     else
2493         m.setMessageClass( -1 );
2494
2495     // Get the timestamp (deliver) or validity period (submit) information.
2496     if ( msgType == SMS_Deliver ) {
2497         m.setTimestamp( timeStamp() );
2498     } else {
2499         if ( validityFormat == SMS_VF_Relative ) {
2500             if ( !needOctets(1) )
2501                 return m;
2502             m.setGsmValidityPeriod( bits(0, 8) );
2503             skipOctet();
2504         } else if (validityFormat == SMS_VF_Absolute ) {
2505             m.setTimestamp( timeStamp() );
2506         } else if (validityFormat == SMS_VF_Enhanced ) {
2507             // Not supported yet - skip the octets.
2508             if ( !needOctets(7) )
2509                 return m;
2510             mPosn += 7;
2511         } else {
2512             m.setValidityPeriod( (uint)(-1) );
2513         }
2514     }
2515
2516     // Read the user data field.
2517     QByteArray *headers = 0;
2518     QString text;
2519     text = userData( (QSMSDataCodingScheme)scheme, codec,
2520                      headers, userDataHeader, false );
2521     if ( !headers && text.startsWith( "//SCKL" ) ) {
2522         unpackSckl( m, text );
2523         return m;
2524     }
2525     if ( headers ) {
2526         m.setHeaders( *headers );
2527         delete headers;
2528         m.unpackHeaderParts();
2529         if ( isSMSDatagram( m.d->mHeaders ) && mPosn <= mBuffer.size() ) {
2530             // The rest of the PDU is assumed to be the application payload.
2531             QByteArray array = mBuffer.mid( mPosn );
2532             m.addPart( QSMSMessagePart( "application/x-qtopia-wdp", array ) );
2533         }
2534     }
2535     if ( text.length() > 0 ) {
2536         m.addPart( QSMSMessagePart( text ) );
2537     }
2538
2539     // Return the completed message to the caller.
2540     return m;
2541 }
2542
2543
2544 QCBSDeliverMessage::QCBSDeliverMessage()
2545     : QPDUMessage()
2546 {
2547     // Nothing to do here.
2548 }
2549
2550
2551 QCBSDeliverMessage::QCBSDeliverMessage(const QByteArray &pdu)
2552     : QPDUMessage(pdu)
2553 {
2554     // Nothing to do here.
2555 }
2556
2557
2558 QCBSMessage QCBSDeliverMessage::unpack(QTextCodec *codec)
2559 {
2560     QCBSMessage m;
2561     unsigned char scheme;
2562     uint len;
2563
2564     // Start from the beginning of the PDU.
2565     reset();
2566
2567     // Extract the header fields.
2568     if ( !needOctets(6) )
2569         return m;
2570
2571     const char *mOffset = mBuffer.constData() + mPosn;
2572     m.setMessageCode( ((mOffset[0] & 0xFC) << 2) | (mOffset[1] & 0x0F) );
2573     m.setScope( (QCBSMessage::GeographicalScope)(mOffset[0] & 0x03) );
2574     m.setUpdateNumber( (mOffset[1] >> 4) & 0x0F );
2575     m.setChannel( ((mOffset[2] & 0xFF) << 8) | (mOffset[3] & 0xFF) );
2576     scheme = (unsigned char)((mOffset[4] >> 4) & 0x0F);
2577     m.setLanguage( (QCBSMessage::Language)(mOffset[4] & 0x0F) );
2578     m.setNumPages( (uint)((mOffset[5] >> 4) & 0x0F) );
2579     m.setPage( (uint)(mOffset[5] & 0x0F) );
2580     mPosn += 6;
2581
2582     // Read the user data field and strip CR's, LF's, and NUL's from the end.
2583     QByteArray *headers = 0;
2584     QString text = userData
2585         ( (QSMSDataCodingScheme)scheme, codec, headers, false, true );
2586     len = text.length();
2587     while ( len > 0 && ( text[len - 1] == '\r' || text[len - 1] == '\n' ||
2588                          text[len - 1] == '\0' ) ) {
2589         --len;
2590     }
2591     m.setText( text.left( len ) );
2592
2593     // Return the completed message to the caller.
2594     return m;
2595 }
2596
2597 void QCBSDeliverMessage::pack(const QCBSMessage &m, QSMSDataCodingScheme scheme)
2598 {
2599     // Clear the pdu before we start.
2600     mBuffer = QByteArray();
2601     mPosn = 0;
2602     mBits = 0;
2603
2604     scheme = QSMS_8BitAlphabet; // Only 8-bit works at present.
2605     QByteArray data;
2606     mBuffer.append( (char)( ((m.messageCode() & 0x000003F0)>>2) | (m.scope() & 0x03)) );
2607     mBuffer.append( (char)((m.messageCode() & 0x0000000F) | (m.updateNumber() & 0x0000000F)<<4) );
2608     mBuffer.append( (char)((m.channel() & 0x0000FF00) >> 8) );
2609     mBuffer.append( (char)(m.channel() & 0x000000FF) );
2610     mBuffer.append( (char)( ((scheme & 0x0F)<<4) | (m.language() & 0x0F)) );
2611     mBuffer.append( (char)( ((m.numPages() & 0x0F)<<4) | (m.page() & 0x0F)) );
2612
2613     QTextCodec *codec = QAtUtils::codec( "gsm" );
2614     QByteArray header;
2615     setUserData(m.text(), scheme, codec, header,true);
2616     int numPad = 88 - (m.text().length()  + data.size());
2617
2618     for ( int i=0; i<numPad; i++ )
2619         appendOctet(0x0D);
2620 }
2621
2622 Q_IMPLEMENT_USER_METATYPE(QSMSMessage)
2623 Q_IMPLEMENT_USER_METATYPE(QSMSMessagePart)