|
1 // Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // Implements Segmentation and Reassembly classes |
|
15 // |
|
16 // |
|
17 |
|
18 /** |
|
19 @file |
|
20 */ |
|
21 |
|
22 #include "gsmusar.h" |
|
23 #include "Gsmumain.h" |
|
24 |
|
25 // |
|
26 // CSmsBufferSegmenter - segments and reassembles (unpacked) data to and from CSmsBufferBase |
|
27 // |
|
28 |
|
29 /** |
|
30 * This factory function creates a CsmsBufferSegmenter instance |
|
31 * |
|
32 * @param aAlphabetConverter Pre-configured |
|
33 * @param aBuffer The message data |
|
34 * @param aSegmentSize (Default = 0) The size of segment required. If the instance is to be used solely for determining the total length of the converted data with TotalConvertedLengthL() then the segment length need not be supplied, hence defaulting to zero. |
|
35 * @return CSmsBufferSegmenter instance |
|
36 * |
|
37 * @capability None |
|
38 */ |
|
39 EXPORT_C CSmsBufferSegmenter* CSmsBufferSegmenter::NewLC(CSmsAlphabetConverter& aAlphabetConverter,const CSmsBufferBase& aBuffer,TInt aSegmentSize) |
|
40 { |
|
41 LOGGSMU1("CSmsBufferSegmenter::NewLC()"); |
|
42 |
|
43 CSmsBufferSegmenter* segmenter=new(ELeave) CSmsBufferSegmenter(aAlphabetConverter,aBuffer,aSegmentSize); |
|
44 CleanupStack::PushL(segmenter); |
|
45 segmenter->ConstructL(); |
|
46 return segmenter; |
|
47 } // CSmsBufferSegmenter::NewLC |
|
48 |
|
49 |
|
50 CSmsBufferSegmenter::CSmsBufferSegmenter(CSmsAlphabetConverter& aAlphabetConverter,const CSmsBufferBase& aBuffer,TInt aSegmentSize) |
|
51 : iAlphabetConverter(aAlphabetConverter),iSmsBuffer(aBuffer),iSegmentSize(aSegmentSize),iConvertedBufferPtr(NULL,0) |
|
52 { |
|
53 // NOP |
|
54 } // CSmsBufferSegmenter::CSmsBufferSegmenter |
|
55 |
|
56 |
|
57 /** |
|
58 * 2nd phase of constructor. Esures we have an initial buffer. |
|
59 */ |
|
60 void CSmsBufferSegmenter::ConstructL() |
|
61 { |
|
62 LOGGSMU1("CSmsBufferSegmenter::ConstructL()"); |
|
63 |
|
64 iConvertedBuffer=HBufC8::NewMaxL(iSegmentSize); |
|
65 iConvertedBufferPtr.Set((TUint8*)iConvertedBuffer->Des().Ptr(),0,iConvertedBuffer->Length()); |
|
66 } // CSmsBufferSegmenter::ConstructL |
|
67 |
|
68 |
|
69 /** |
|
70 * Destructor - Free resource |
|
71 * |
|
72 * @capability None |
|
73 */ |
|
74 EXPORT_C CSmsBufferSegmenter::~CSmsBufferSegmenter() |
|
75 { |
|
76 delete iConvertedBuffer; |
|
77 } // CSmsBufferSegmenter::ConstructL |
|
78 |
|
79 |
|
80 /** |
|
81 * Resets the converted buffer |
|
82 */ |
|
83 void CSmsBufferSegmenter::Reset() |
|
84 { |
|
85 LOGGSMU1("CSmsBufferSegmenter::Reset()"); |
|
86 |
|
87 iConvertedBufferPtr.Zero(); |
|
88 iElementsExtracted=0; |
|
89 } // CSmsBufferSegmenter::Reset |
|
90 |
|
91 |
|
92 /** |
|
93 * A client iteratively calls SegmentNextL() to retrieve each segment until ETrue is returned, indicating |
|
94 * that the last segment has been reached. The output buffer, aSegmentBuffer, should be large enough to hold |
|
95 * the number of elements specified by the segment size in the NewLC() constructor otherwise the call will |
|
96 * Panic. The output buffer is automatically reset before it's filled. |
|
97 * |
|
98 * @param aSegmentBuffer The next segment |
|
99 * @return True if the last segment has been reached |
|
100 * |
|
101 * @capability None |
|
102 */ |
|
103 EXPORT_C TBool CSmsBufferSegmenter::SegmentNextL(TDes8& aSegmentBuffer, |
|
104 TInt& aUnconvertedChars, TInt& aDowngradedChars, |
|
105 TSmsEncoding aEncoding) |
|
106 { |
|
107 LOGGSMU2("CSmsBufferSegmenter::SegmentNextL(): iSegmentSize=%d", iSegmentSize); |
|
108 |
|
109 TBool ret = DoSegmentNextL(aSegmentBuffer, iSegmentSize, |
|
110 aUnconvertedChars, aDowngradedChars, |
|
111 aEncoding); |
|
112 LOGGSMU2("CSmsBufferSegmenter::SegmentNextL() returns %d ", ret); |
|
113 |
|
114 return ret; |
|
115 } // CSmsBufferSegmenter::SegmentNextL |
|
116 |
|
117 |
|
118 TBool CSmsBufferSegmenter::DoSegmentNextL(TDes8& aSegmentBuffer,TInt aSegmentSize, |
|
119 TInt& aUnconvertedChars, TInt& aDowngradedChars, |
|
120 TSmsEncoding aEncoding) |
|
121 // |
|
122 // Extracts a "native" segment from the SMS buffer, converts to the required |
|
123 // character set and breaks off the next segment of required size. |
|
124 // Returns true if this was the last segment |
|
125 // |
|
126 { |
|
127 LOGGSMU2("CSmsBufferSegmenter::DoSegmentNextL(): aSegmentSize=%d", aSegmentSize); |
|
128 |
|
129 __ASSERT_ALWAYS(aSegmentSize>0,Panic(KGsmuPanicIllegalSegmentSize)); |
|
130 __ASSERT_ALWAYS(aSegmentBuffer.MaxLength()>=aSegmentSize,Panic(KGsmuPanicSegmentBufferTooSmall)); |
|
131 |
|
132 // Extract from buffer until we have enough chars for a segment or we're at the end |
|
133 aSegmentBuffer.Zero(); |
|
134 TBuf<CSmsBufferBase::EMaxBufLength> nativeChars; |
|
135 while ((iConvertedBufferPtr.Length()<aSegmentSize)&&(iElementsExtracted<iSmsBuffer.Length())) |
|
136 { |
|
137 TInt elementsToExtract=Min(static_cast<TInt>(CSmsBufferBase::EMaxBufLength),iSmsBuffer.Length()-iElementsExtracted); |
|
138 iSmsBuffer.Extract(nativeChars,iElementsExtracted,elementsToExtract); |
|
139 |
|
140 TInt numberOfUnconvertibleCharacters; |
|
141 TInt numberOfDowngradedCharacters; |
|
142 TPtrC8 smsCharsPtr=iAlphabetConverter.ConvertFromNativeL(nativeChars, |
|
143 aEncoding, |
|
144 numberOfUnconvertibleCharacters, |
|
145 numberOfDowngradedCharacters); |
|
146 aUnconvertedChars += numberOfUnconvertibleCharacters; |
|
147 aDowngradedChars += numberOfDowngradedCharacters; |
|
148 |
|
149 CheckConvertedBufferAllocL(iConvertedBufferPtr.Length()+smsCharsPtr.Length()); |
|
150 iConvertedBufferPtr.Append(smsCharsPtr); |
|
151 iElementsExtracted+=elementsToExtract; |
|
152 } |
|
153 // Determine number of converted elements to put in this segment |
|
154 TInt elementsInSegment=ElementsToReturnFromConvertedBufferL(aSegmentSize); |
|
155 // And copy... |
|
156 aSegmentBuffer.Copy(iConvertedBufferPtr.Ptr(),elementsInSegment); |
|
157 iConvertedBufferPtr.Delete(0,elementsInSegment); |
|
158 // If this is the last segment then ensure no unconverted characters remain |
|
159 return !MoreL(); |
|
160 |
|
161 } // CSmsBufferSegmenter::DoSegmentNextL |
|
162 |
|
163 |
|
164 TBool CSmsBufferSegmenter::MoreL() |
|
165 { |
|
166 LOGGSMU1("CSmsBufferSegmenter::MoreL()"); |
|
167 |
|
168 if ((iElementsExtracted>=iSmsBuffer.Length())&&(iConvertedBufferPtr.Length()==0)) |
|
169 { |
|
170 if (iAlphabetConverter.UnconvertedNativeCharacters().Length()>0) |
|
171 { |
|
172 User::Leave(KErrCorrupt); |
|
173 } |
|
174 |
|
175 return EFalse; |
|
176 } |
|
177 |
|
178 return ETrue; |
|
179 } // CSmsBufferSegmenter::MoreL |
|
180 |
|
181 |
|
182 /** |
|
183 * TotalConvertedLengthL() determines the total length in User Data Elements when the input buffer is converted. |
|
184 * Depending on the conversion properties returned from the alphabet converter, a complete conversion may need |
|
185 * to be performed, i.e. this call can be quite expensive. |
|
186 * |
|
187 * @return Total converted length |
|
188 * |
|
189 * @capability None |
|
190 */ |
|
191 EXPORT_C TInt CSmsBufferSegmenter::TotalConvertedLengthL(TSmsEncoding aEncoding) |
|
192 { |
|
193 LOGGSMU2("CSmsBufferSegmenter::TotalConvertedLengthL(): aEncoding=%d", aEncoding); |
|
194 |
|
195 // Check for shortcut |
|
196 CSmsAlphabetConverter::TSmsAlphabetConversionProperties conversionProperties; |
|
197 iAlphabetConverter.ConversionPropertiesL(conversionProperties); |
|
198 if (conversionProperties.iWidthConversion==CSmsAlphabetConverter::ESmsAlphabetWidthConversionFixed) |
|
199 return iSmsBuffer.Length()*conversionProperties.iUDElementsPerNativeCharacter; |
|
200 |
|
201 // No shortcut, have to do piecewise conversion |
|
202 Reset(); |
|
203 |
|
204 // |
|
205 // Find the best encoding method to use... |
|
206 // |
|
207 TSmsEncoding aEncodingToUse = FindBestAlternativeEncodingL(aEncoding, iSmsBuffer.Length()); |
|
208 |
|
209 // |
|
210 // Segment and count the converted length... |
|
211 // |
|
212 TBool complete=EFalse; |
|
213 TInt totalConvertedLength=0; |
|
214 TInt unconvertedChars=0; |
|
215 TInt downgradedChars=0; |
|
216 TBuf8<CSmsBufferBase::EMaxBufLength> convertedChars; |
|
217 while (!complete) |
|
218 { |
|
219 complete=DoSegmentNextL(convertedChars,convertedChars.MaxLength(), |
|
220 unconvertedChars, downgradedChars, |
|
221 aEncodingToUse); |
|
222 totalConvertedLength+=convertedChars.Length(); |
|
223 } |
|
224 Reset(); |
|
225 return totalConvertedLength; |
|
226 } // CSmsBufferSegmenter::TotalConvertedLengthL |
|
227 |
|
228 |
|
229 TSmsEncoding CSmsBufferSegmenter::FindBestAlternativeEncodingL(TSmsEncoding aSuggestedEncoding, |
|
230 TInt aMaxBodyLength) const |
|
231 { |
|
232 LOGGSMU3("CSmsBufferSegmenter::FindBestAlternativeEncodingL(): aSuggestedEncoding=%d, aMaxBodyLength=%d", |
|
233 aSuggestedEncoding, aMaxBodyLength); |
|
234 |
|
235 TSmsEncoding encodingToUse = ESmsEncodingNone; |
|
236 |
|
237 // |
|
238 // If this is not 7bit or the alternative encoding is not set then do |
|
239 // nothing... |
|
240 // |
|
241 if (aSuggestedEncoding != ESmsEncodingNone && |
|
242 iAlphabetConverter.Alphabet() == TSmsDataCodingScheme::ESmsAlphabet7Bit) |
|
243 { |
|
244 // |
|
245 // Allocate a buffer and extract the text and ask the converter for |
|
246 // the best encoding... |
|
247 // |
|
248 HBufC* buffer = HBufC::NewLC(aMaxBodyLength); |
|
249 TPtr bufferPtr = buffer->Des(); |
|
250 TInt elementsToExtract = Min(aMaxBodyLength, iSmsBuffer.Length() - iElementsExtracted); |
|
251 |
|
252 iSmsBuffer.Extract(bufferPtr, iElementsExtracted, elementsToExtract); |
|
253 |
|
254 encodingToUse = iAlphabetConverter.FindBestAlternativeEncodingL(bufferPtr, |
|
255 aSuggestedEncoding); |
|
256 |
|
257 CleanupStack::PopAndDestroy(buffer); |
|
258 } |
|
259 |
|
260 return encodingToUse; |
|
261 } // CSmsBufferSegmenter::FindBestAlternativeEncodingL |
|
262 |
|
263 |
|
264 /** |
|
265 * Ensures the segmentation buffer is of the specified length |
|
266 */ |
|
267 void CSmsBufferSegmenter::CheckConvertedBufferAllocL(TInt aMaxLength) |
|
268 { |
|
269 LOGGSMU2("CSmsBufferSegmenter::CheckConvertedBufferAllocL(): aMaxLength=%d", |
|
270 aMaxLength); |
|
271 |
|
272 if (iConvertedBuffer->Length()<aMaxLength) |
|
273 { |
|
274 iConvertedBuffer=iConvertedBuffer->ReAllocL(aMaxLength); |
|
275 iConvertedBuffer->Des().SetLength(aMaxLength); |
|
276 iConvertedBufferPtr.Set((TUint8*)iConvertedBuffer->Des().Ptr(),iConvertedBufferPtr.Length(),iConvertedBuffer->Length()); |
|
277 } |
|
278 } // CSmsBufferSegmenter::CheckConvertedBufferAllocL |
|
279 |
|
280 |
|
281 /** |
|
282 * Determines the number of converted elements that should be returned in the |
|
283 * segment - called from DoSegmentNextL |
|
284 */ |
|
285 TInt CSmsBufferSegmenter::ElementsToReturnFromConvertedBufferL(TInt aSegmentSize) |
|
286 { |
|
287 LOGGSMU2("CSmsBufferSegmenter::CheckConvertedBufferAllocL(): aSegmentSize=%d", |
|
288 aSegmentSize); |
|
289 |
|
290 TInt elementCount=Min(aSegmentSize,iConvertedBufferPtr.Length()); |
|
291 if (iAlphabetConverter.Alphabet()==TSmsDataCodingScheme::ESmsAlphabet7Bit) |
|
292 { |
|
293 // For 7-bit, don't break an extended character across a segment |
|
294 while ((elementCount>0)&&(iConvertedBufferPtr[elementCount-1]==KSms7BitAlphabetEscapeChar)) |
|
295 --elementCount; |
|
296 if ((elementCount>0)&&(iConvertedBufferPtr[elementCount-1]==KSms7BitAlphabetEscapeChar)) |
|
297 User::Leave(KErrCorrupt); |
|
298 } |
|
299 return elementCount; |
|
300 } // CSmsBufferSegmenter::ElementsToReturnFromConvertedBufferL |
|
301 |
|
302 |
|
303 //------------------------------------------------------------------------------------------------------------ |
|
304 |
|
305 |
|
306 /** |
|
307 * @capability None |
|
308 */ |
|
309 EXPORT_C CSmsEMSBufferSegmenter* CSmsEMSBufferSegmenter::NewLC(CSmsAlphabetConverter& aAlphabetConverter,const CSmsBufferBase& aBuffer, TInt aSegmentSize) |
|
310 { |
|
311 LOGGSMU2("CSmsBufferSegmenter::NewLC(): aSegmentSize=%d", aSegmentSize); |
|
312 |
|
313 CSmsEMSBufferSegmenter* self = new (ELeave) CSmsEMSBufferSegmenter(aAlphabetConverter, aBuffer, aSegmentSize); |
|
314 CleanupStack::PushL(self); |
|
315 self->ConstructL(); |
|
316 return self; |
|
317 } // CSmsEMSBufferSegmenter::NewLC |
|
318 |
|
319 |
|
320 CSmsEMSBufferSegmenter::CSmsEMSBufferSegmenter(CSmsAlphabetConverter& aAlphabetConverter,const CSmsBufferBase& aBuffer,TInt aSegmentSize) : |
|
321 CSmsBufferSegmenter(aAlphabetConverter, aBuffer, aSegmentSize) |
|
322 { |
|
323 // NOP |
|
324 } // CSmsEMSBufferSegmenter::CSmsEMSBufferSegmenter |
|
325 |
|
326 |
|
327 /** |
|
328 * Extracts Segment size out of the Sms Buffer and converts it into aSegmentBuffer |
|
329 * |
|
330 * @return aSegmentBuffer - Buffer to convert into |
|
331 * @param aSegement - Size of the FOREIGN segment to extract |
|
332 * @return ETrue if last segment |
|
333 * |
|
334 * @capability None |
|
335 */ |
|
336 EXPORT_C TBool CSmsEMSBufferSegmenter::SegmentNextL(TDes8& aSegmentBuffer, TInt aSegmentSize, |
|
337 TInt& aUnconvertedChars, TInt& aDowngradedChars, |
|
338 TSmsEncoding aEncoding) |
|
339 { |
|
340 LOGGSMU2("CSmsEMSBufferSegmenter::SegmentNext(): aSegmentSize=%d", aSegmentSize); |
|
341 |
|
342 TBool ret=DoSegmentNextL(aSegmentBuffer, aSegmentSize, aUnconvertedChars, aDowngradedChars, |
|
343 aEncoding); |
|
344 |
|
345 LOGGSMU2("CSmsEMSBufferSegmenter::SegmentNext() returns %d ", ret); |
|
346 |
|
347 return ret; |
|
348 } // CSmsEMSBufferSegmenter::SegmentNextL |
|
349 |
|
350 |
|
351 /** |
|
352 * SegmentL encodes the amount of native chars into a SegmentBuffer |
|
353 * WARNING: This method can not be used after a SegmentNextL |
|
354 * |
|
355 * @return aSegmentBuffer - Buffer to convert into. |
|
356 * @param aNativeChars - Number of native chars to encode. |
|
357 * @param aSegmentMax - The Ceiling the encode will not go past. |
|
358 * @return The number of NATIVE characters added |
|
359 */ |
|
360 TInt CSmsEMSBufferSegmenter::SegmentL(TDes8& aSegmentBuffer, TInt aNativeChars, TInt aSegmentMax, |
|
361 TInt& aUnconvertedChars, TInt& aDowngradedChars, |
|
362 TSmsEncoding aEncoding) |
|
363 { |
|
364 LOGGSMU3("CSmsEMSBufferSegmenter::SegmentL(): aNativeChars=%d, aSegmentMax=%d", |
|
365 aNativeChars, aSegmentMax); |
|
366 |
|
367 __ASSERT_ALWAYS(iConvertedBufferPtr.Length()==0, User::Leave(KGsmuPanicBufferNotReset)); |
|
368 __ASSERT_ALWAYS(aNativeChars>0,User::Leave(KGsmuPanicIllegalSegmentSize)); |
|
369 __ASSERT_ALWAYS(aSegmentMax>0,User::Leave(KGsmuPanicIllegalSegmentSize)); |
|
370 __ASSERT_ALWAYS(aSegmentBuffer.MaxLength()>=aNativeChars,User::Leave(KGsmuPanicSegmentBufferTooSmall)); |
|
371 |
|
372 // Extract native chars from the buffer and convert |
|
373 aSegmentBuffer.Zero(); |
|
374 TBuf<CSmsBufferBase::EMaxBufLength> nativeChars; |
|
375 TInt nativeElemsToExtract=aNativeChars; |
|
376 nativeElemsToExtract=Min(nativeElemsToExtract,nativeChars.MaxLength()); |
|
377 do |
|
378 { |
|
379 iSmsBuffer.Extract(nativeChars,iElementsExtracted, nativeElemsToExtract); |
|
380 |
|
381 TInt numberOfUnconvertibleCharacters; |
|
382 TInt numberOfDowngradedCharacters; |
|
383 TPtrC8 smsCharsPtr=iAlphabetConverter.ConvertFromNativeL(nativeChars, |
|
384 aEncoding, |
|
385 numberOfUnconvertibleCharacters, |
|
386 numberOfDowngradedCharacters); |
|
387 aUnconvertedChars += numberOfUnconvertibleCharacters; |
|
388 aDowngradedChars += numberOfDowngradedCharacters; |
|
389 |
|
390 if (smsCharsPtr.Length()>aSegmentMax) |
|
391 --nativeElemsToExtract; |
|
392 else |
|
393 { |
|
394 iElementsExtracted += nativeElemsToExtract; |
|
395 aSegmentBuffer.Copy(smsCharsPtr.Ptr(),smsCharsPtr.Length()); |
|
396 return nativeElemsToExtract; |
|
397 } |
|
398 } while (nativeElemsToExtract); |
|
399 |
|
400 return 0; |
|
401 } // CSmsEMSBufferSegmenter::SegmentL |
|
402 |
|
403 |
|
404 //------------------------------------------------------------------------------------------------------------ |
|
405 |
|
406 |
|
407 /** |
|
408 * |
|
409 * Constructor initialise iAlphabetConverter & iSmsBuffer |
|
410 * |
|
411 * @capability None |
|
412 */ |
|
413 EXPORT_C TSmsBufferReassembler::TSmsBufferReassembler(CSmsAlphabetConverter& aAlphabetConverter,CSmsBufferBase& aBuffer) |
|
414 : iAlphabetConverter(aAlphabetConverter),iSmsBuffer(aBuffer) |
|
415 { |
|
416 // NOP |
|
417 } // TSmsBufferReassembler::TSmsBufferReassembler |
|
418 |
|
419 |
|
420 /** |
|
421 * Reassembly is performed by iteratively calling the ReassembleNextL() method until all segments have been |
|
422 * passed in. When the last segment is passed, the aIsLast flag should be set to ETrue to validate that no |
|
423 * unconverted User Data Elements remain, otherwise the method will leave with KErrCorrupt. |
|
424 * |
|
425 * @param aSegmentBuffer A segmented buffer |
|
426 * @param aIsLast Set to true if it's the last segment |
|
427 * |
|
428 * @capability None |
|
429 */ |
|
430 EXPORT_C void TSmsBufferReassembler::ReassembleNextL(const TDesC8& aSegmentBuffer, |
|
431 TSmsEncoding aEncoding,TBool aIsLast) |
|
432 { |
|
433 LOGGSMU3("TSmsBufferReassembler::ReassembleNextL(): aEncoding=%d aIsLast=%d", |
|
434 aEncoding, aIsLast); |
|
435 |
|
436 TPtrC nativeChars=iAlphabetConverter.ConvertToNativeL(aSegmentBuffer, aEncoding); |
|
437 iSmsBuffer.InsertL(iSmsBuffer.Length(),nativeChars); |
|
438 if ((aIsLast)&&(iAlphabetConverter.UnconvertedUDElements().Length()>0)) |
|
439 { |
|
440 User::Leave(KErrCorrupt); |
|
441 } |
|
442 } // TSmsBufferReassembler::ReassembleNextL |