|
1 // Copyright (c) 1997-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 // This file contains the implementation of the TWapTextMessage. |
|
15 // TWapTextMessage encodes and decodes 7-bit WAP text headers |
|
16 // |
|
17 // |
|
18 |
|
19 /** |
|
20 @file |
|
21 */ |
|
22 |
|
23 #include <e32std.h> |
|
24 #include "wapthdr.h" |
|
25 #include "ws_main.h" |
|
26 |
|
27 const TInt TWapTextMessage::KHeaderCount = 6; |
|
28 |
|
29 _LIT8(KSCKHeaderLong, "//SCKL"); // standard NBS header for 16-bit adresses |
|
30 _LIT8(KSCKHeaderShort,"//SCK"); // standard NBS header for 8-bit adresses |
|
31 |
|
32 |
|
33 /** |
|
34 * Indices into different types header for each of the header elements |
|
35 * (dest port, source port, reference, total segments, current segment) |
|
36 */ |
|
37 const TWapTextMessage::TIndexInfo TWapTextMessage::KElemIndexes[TWapTextMessage::KHeaderCount] = |
|
38 { |
|
39 // Long header ("//SCKL") indices |
|
40 // destination port (, other header) |
|
41 { 1,{ 6, 10, 0, 0, 0, 0} }, |
|
42 // destination port, source port (, other header) |
|
43 { 2,{ 6, 10, 14, 0, 0, 0} }, |
|
44 // destination port, source port, |
|
45 // reference, total segments, current segment(, other header) |
|
46 { 5,{ 6, 10, 14, 16, 18, 20}}, |
|
47 |
|
48 // Short header ("//SCK") indices |
|
49 // destination port (, other header) |
|
50 { 1, { 5, 7, 0, 0, 0, 0}}, |
|
51 // destination port, source port (, other header) |
|
52 { 2, { 5, 7, 9, 0, 0, 0}}, |
|
53 // destination port, source port, |
|
54 // reference, total segments, current segment(, other header) |
|
55 { 5,{ 5, 7, 9, 11, 13, 15}}, |
|
56 }; |
|
57 |
|
58 |
|
59 /** |
|
60 * C'tor |
|
61 * |
|
62 * @param aWapMessage WAP message to decode/encode |
|
63 * @note simply use KNullDesC8, if you are going to encode. |
|
64 */ |
|
65 TWapTextMessage::TWapTextMessage(const TDesC8& aWapMessage) |
|
66 :iIsWapTextMessage(EFalse) |
|
67 ,iWAPMessage(aWapMessage) |
|
68 ,iIs16Bit(EFalse) |
|
69 ,iDestinationPort(-1) |
|
70 ,iSourcePort(-1) |
|
71 ,iReference(0) |
|
72 ,iTotalSegments(0) |
|
73 ,iSegmentNumber(0) |
|
74 ,iOtherHeader(0) |
|
75 ,iOtherHeaderLength(0) |
|
76 ,iData(0) |
|
77 ,iDataLength(0) |
|
78 ,iRefOtherHeader(KNullDesC8) |
|
79 ,iRefData(KNullDesC8) |
|
80 { |
|
81 } // TWapTextMessage::TWapTextMessage |
|
82 |
|
83 |
|
84 /** |
|
85 * Panic is raised, if the header with other header and terminating |
|
86 * ' ' is > 159 characters. Then 1 byte is left for payload ! |
|
87 * what will be the array element type ? |
|
88 */ |
|
89 void TWapTextMessage::EncodeSegmentsL(CArrayPtr<HBufC8>& aSegmentArray) |
|
90 { |
|
91 TBuf8<KMaxSmsChars> header; // buffer for a modifiable header |
|
92 TInt headerLength = 0; |
|
93 TInt segmentNumberIndex = 0; // index of segment number field in the header |
|
94 TBuf8<2> hexSegmentNumber; |
|
95 TInt dataSegmented = 0; |
|
96 |
|
97 LOGWAPPROT1("TWapTextMessage::EncodeSegmentsL()"); |
|
98 |
|
99 iSegmentNumber = 0; |
|
100 do |
|
101 { |
|
102 // |
|
103 // Create the segment and add it to the array... |
|
104 // |
|
105 HBufC8* segment = HBufC8::NewL(KMaxSmsChars); // 160 characters |
|
106 CleanupStack::PushL(segment); |
|
107 TPtr8 ptr(segment->Des()); |
|
108 aSegmentArray.AppendL(segment); |
|
109 CleanupStack::Pop(); // segment |
|
110 |
|
111 // |
|
112 // Calculate length of header and copy it... |
|
113 // |
|
114 if (iSegmentNumber==0) |
|
115 { |
|
116 headerLength = CreateHeader(header, segmentNumberIndex); |
|
117 } |
|
118 |
|
119 ptr.Copy(header); |
|
120 if (iTotalSegments>255) |
|
121 { |
|
122 User::Leave(KErrOverflow); |
|
123 } |
|
124 |
|
125 // |
|
126 // Set segment number... |
|
127 // |
|
128 if (segmentNumberIndex != 0) |
|
129 { |
|
130 hexSegmentNumber.NumFixedWidthUC(iSegmentNumber+1, EHex, 2); // two bytes wide |
|
131 ptr.Insert(segmentNumberIndex, hexSegmentNumber); |
|
132 } |
|
133 |
|
134 // |
|
135 // Count any escaped characters we can be sure that the converted data |
|
136 // size fits inside the remaining length (e.g. so that non-7bit characters |
|
137 // when converted by the SMS Stack will still fit). |
|
138 // |
|
139 TInt segmentSize = iRefData.Length() - dataSegmented; |
|
140 |
|
141 if (segmentSize > KMaxSmsChars - headerLength) |
|
142 { |
|
143 segmentSize = KMaxSmsChars - headerLength; |
|
144 } |
|
145 |
|
146 while (segmentSize > 1) |
|
147 { |
|
148 TPtrC8 segmentData(iRefData.Mid(dataSegmented, segmentSize)); |
|
149 TInt non7bitCharEscapes = 0; |
|
150 |
|
151 // |
|
152 // Count all non-7bit characters that will be escaped (many non-7bit |
|
153 // characters are not escaped, but converted to "?"). The ones |
|
154 // that are known to be escaped are list below: |
|
155 // |
|
156 // 12 [Form Feed]. |
|
157 // 91 "[" |
|
158 // 92 "\" |
|
159 // 93 "]" |
|
160 // 94 "^" |
|
161 // 123 "{" |
|
162 // 124 "|" |
|
163 // 125 "}" |
|
164 // 126 "~" |
|
165 // |
|
166 for (TInt ch = 0; ch < segmentSize; ch++) |
|
167 { |
|
168 if (segmentData[ch] == 12 || |
|
169 (segmentData[ch] >= 91 && segmentData[ch] <= 94) || |
|
170 (segmentData[ch] >= 123 && segmentData[ch] <= 126)) |
|
171 { |
|
172 non7bitCharEscapes++; |
|
173 } |
|
174 } |
|
175 |
|
176 // |
|
177 // Can it fit? If so store it, otherwise reduce the size... |
|
178 // |
|
179 if (segmentData.Length() + non7bitCharEscapes <= KMaxSmsChars - headerLength) |
|
180 { |
|
181 ptr.Append(segmentData); |
|
182 break; |
|
183 } |
|
184 |
|
185 segmentSize--; |
|
186 } |
|
187 |
|
188 dataSegmented += segmentSize; |
|
189 iSegmentNumber++; |
|
190 } |
|
191 while (dataSegmented < iRefData.Length()); |
|
192 |
|
193 __ASSERT_DEBUG(iTotalSegments == aSegmentArray.Count(), Panic(KPanicEncodingError)); |
|
194 } // TWapTextMessage::EncodeSegmentsL |
|
195 |
|
196 |
|
197 /** |
|
198 * Returns true, if the short message starts with |
|
199 * WAP text message header set by SetWapTextMessage. |
|
200 */ |
|
201 TBool TWapTextMessage::Parse() |
|
202 { |
|
203 TInt waplength = iWAPMessage.Length(); |
|
204 LOGWAPPROT2("TWapTextMessage::Parse [%d bytes]", waplength); |
|
205 if(waplength != 0) |
|
206 { |
|
207 // check whether long or short header |
|
208 TInt iBaseIndex = -1; |
|
209 |
|
210 // minimum length is 8 "//SCKxx " |
|
211 if(waplength >= 8) |
|
212 { |
|
213 TPtrC8 HeaderIdPart(iWAPMessage.Left(6)); |
|
214 if(!HeaderIdPart.Compare(KSCKHeaderLong)) |
|
215 { |
|
216 iBaseIndex = 0; |
|
217 iIs16Bit = ETrue; // Yes, the ports are 16 bit wide |
|
218 } |
|
219 else |
|
220 HeaderIdPart.Set(iWAPMessage.Left(5)); |
|
221 |
|
222 if(iBaseIndex == (-1) && !HeaderIdPart.Compare(KSCKHeaderShort) ) |
|
223 iBaseIndex = 3; |
|
224 // else no match |
|
225 } |
|
226 |
|
227 if(iBaseIndex >= 0) |
|
228 { |
|
229 // check which of the TIndexInfos match |
|
230 for(TInt i=iBaseIndex; i<iBaseIndex+3; i++) |
|
231 { |
|
232 // Get length (index) of text header variant |
|
233 TInt lastIndex = KElemIndexes[i].iIndexes[KElemIndexes[i].iLastIndex]; |
|
234 if (iWAPMessage.Length() <= lastIndex) |
|
235 { |
|
236 // no hope about match, because other variants are longer |
|
237 iIsWapTextMessage = EFalse; |
|
238 break; |
|
239 } |
|
240 |
|
241 TInt LastChar = iWAPMessage[lastIndex]; |
|
242 if (LastChar == '/' || LastChar == ' ' || LastChar == '\n') |
|
243 { |
|
244 // reinitialize, because following maybe absent |
|
245 iTotalSegments = 1; |
|
246 iSegmentNumber = 1; |
|
247 iIsWapTextMessage = ParseWapTextHeader(KElemIndexes[i]); |
|
248 break; |
|
249 } |
|
250 |
|
251 } |
|
252 |
|
253 if (!iIsWapTextMessage) |
|
254 { |
|
255 LOGWAPPROT1("WARNING! illegal incoming WAP message"); |
|
256 } |
|
257 } |
|
258 } |
|
259 |
|
260 return iIsWapTextMessage; |
|
261 } // TWapTextMessage::Parse |
|
262 |
|
263 |
|
264 /** |
|
265 * Parses a string of hex characters representing a number |
|
266 * |
|
267 * @param aInValue descriptor containing the number |
|
268 * @param aBigEndian true if number is big endian |
|
269 * @param aRadix Radixbase; 16, 10 etc |
|
270 * @return TInt the parsed number |
|
271 * @leave Panics with KPanicInvalidParseNumber error code, if not a |
|
272 * number. Maximum width of the hex value can be 4. |
|
273 * |
|
274 * TODO use TLex instead |
|
275 */ |
|
276 TInt TWapTextMessage::ParseNumber(const TDesC8& aInValue, |
|
277 TBool aBigEndian, |
|
278 TInt aRadix) |
|
279 { |
|
280 LOGWAPPROT2("TWapTextMessage::ParseNumber [%S]", &aInValue); |
|
281 // least significant byte first |
|
282 TInt Values[4] = {0,0,0,0}; |
|
283 TInt Temp = 0; |
|
284 TInt length = aInValue.Length(); |
|
285 TInt i = 0; |
|
286 TInt Value = 0; |
|
287 |
|
288 __ASSERT_DEBUG(length<5,Panic(KPanicInvalidParseNumber)); |
|
289 if( length >= 5 ) |
|
290 return KErrNotFound; |
|
291 for(i=0; i<length; i++) |
|
292 { |
|
293 Temp = aInValue[i]; |
|
294 if (Temp>='0' && Temp<='9') |
|
295 Temp-='0'; |
|
296 else if (Temp>='A' && Temp<='Z') |
|
297 Temp = Temp - 'A'+10; |
|
298 else if (Temp>='a' && Temp<='z') |
|
299 Temp = Temp - 'a'+10; |
|
300 else |
|
301 return KErrNotFound; |
|
302 if (aBigEndian) |
|
303 Values[(length-1)-i]=Temp; |
|
304 else |
|
305 Values[i]=Temp; |
|
306 } |
|
307 |
|
308 // build the value |
|
309 Value=Values[0]; |
|
310 TInt Base=1; |
|
311 for(i=1; i<length; i++) |
|
312 { |
|
313 Base*=aRadix; |
|
314 Value+=(Base)*Values[i]; |
|
315 } |
|
316 |
|
317 return Value; |
|
318 } // TWapTextMessage::ParseNumber |
|
319 |
|
320 |
|
321 /** |
|
322 * parse the WAP text header |
|
323 * |
|
324 * use ElemIndexes to retrieve the index |
|
325 * an starting position of an element in iWapMessage |
|
326 * Length of element is calculated by |
|
327 * subtracting current index value from the next one |
|
328 * except for KOtherHeader and user data, of course |
|
329 */ |
|
330 TBool TWapTextMessage::ParseWapTextHeader(const TIndexInfo& aIndexArray) |
|
331 { |
|
332 LOGWAPPROT2("TWapTextMessage::ParseWapTextHeader: %S", &iWAPMessage ); |
|
333 |
|
334 TInt ParsedNumber = 0; |
|
335 |
|
336 // parse the header |
|
337 TInt ElemIndexCount=aIndexArray.iLastIndex+1; |
|
338 for(TInt i=0; i<ElemIndexCount; i++) |
|
339 { |
|
340 if (i<ElemIndexCount-1) |
|
341 { |
|
342 // all the elems have a length defined in advance |
|
343 if (iWAPMessage.Length() >= aIndexArray.iIndexes[i+1]) |
|
344 { |
|
345 // the header fits into the wap datagram |
|
346 TPtrC8 Elem(iWAPMessage.Mid(aIndexArray.iIndexes[i], |
|
347 aIndexArray.iIndexes[i+1]- |
|
348 aIndexArray.iIndexes[i])); |
|
349 |
|
350 ParsedNumber = ParseNumber(Elem,ETrue,16); |
|
351 if( ParsedNumber == KErrNotFound ) |
|
352 return EFalse; |
|
353 switch(i) |
|
354 { |
|
355 case KIndexDestinationPort: |
|
356 iDestinationPort = ParsedNumber; |
|
357 |
|
358 break; |
|
359 case KIndexSourcePort: |
|
360 iSourcePort = ParsedNumber; |
|
361 break; |
|
362 case KIndexReferenceNumber: |
|
363 iReference = ParsedNumber; |
|
364 break; |
|
365 case KIndexTotalSegments: |
|
366 iTotalSegments = ParsedNumber; |
|
367 break; |
|
368 case KIndexSegmentNumber: |
|
369 iSegmentNumber = ParsedNumber; |
|
370 break; |
|
371 default: |
|
372 LOGWAPPROT2("Hm. unhandled WAP index [%d]", i ); |
|
373 break; |
|
374 } |
|
375 } |
|
376 } |
|
377 else |
|
378 { |
|
379 // elems have not a length defined in advance |
|
380 iOtherHeader = 0; |
|
381 iOtherHeaderLength = 0; |
|
382 |
|
383 // Search the terminating character ' ' |
|
384 iData = iWAPMessage.Locate(' '); |
|
385 TInt dataTmp = iWAPMessage.Locate('\n'); |
|
386 |
|
387 if (iData == KErrNotFound) |
|
388 { |
|
389 if (dataTmp == KErrNotFound) |
|
390 return EFalse; |
|
391 else |
|
392 iData = dataTmp; |
|
393 } |
|
394 else if (dataTmp != KErrNotFound) |
|
395 iData = Min(iData, dataTmp); |
|
396 |
|
397 // check the existence of other header |
|
398 // at least "// " should be there |
|
399 if ( iWAPMessage.Length() > aIndexArray.iIndexes[i]+2 |
|
400 && iWAPMessage[aIndexArray.iIndexes[i]] == '/' |
|
401 && iWAPMessage[aIndexArray.iIndexes[i]+1] == '/') |
|
402 { |
|
403 iOtherHeader = aIndexArray.iIndexes[i]; |
|
404 iOtherHeaderLength=iData-iOtherHeader; |
|
405 } |
|
406 |
|
407 // data: check if any characters after ' ' |
|
408 iDataLength = 0; |
|
409 iData++; |
|
410 if (iWAPMessage.Length() > iData) |
|
411 { |
|
412 iDataLength = iWAPMessage.Length() - iData; |
|
413 } |
|
414 |
|
415 // That's it |
|
416 } // end of other header and data |
|
417 }// end of for loop |
|
418 return ETrue; |
|
419 } // TWapTextMessage::ParseWapTextHeader |
|
420 |
|
421 |
|
422 /** |
|
423 * Length of header is returned |
|
424 * Sets internally the iTotalSegments |
|
425 * Does not set the segment number into the aFixedHeader |
|
426 * |
|
427 * Length of complete header is returned. |
|
428 * On return the header is not complete, if SAR is needed, because the segment |
|
429 * number is not set. Thus aFixedHeader.Length() != (return value of this function) is true. |
|
430 * The segment number can be set by inserting it into aSegmentNumberIndex position |
|
431 * If SAR is not needed aSegmentNumberIndex = 0 and |
|
432 * aFixedHeader.Length() == (return value of this function) is true |
|
433 */ |
|
434 TInt TWapTextMessage::CreateHeader(TDes8& aFixedHeader, TInt& aSegmentNumberIndex) |
|
435 { |
|
436 LOGWAPPROT1("TWapTextMessage::CreateHeader"); |
|
437 |
|
438 // Index into KElemIndexes indicating which header elements are present |
|
439 TInt elemIndex; |
|
440 // Index into Indexes, for current header element |
|
441 TInt minorIndex=KIndexDestinationPort; |
|
442 TBuf8<4> hexNumber; |
|
443 |
|
444 // Segment number length is set, if SAR is needed |
|
445 TInt segmentNumberLength = 0; |
|
446 aSegmentNumberIndex = 0; |
|
447 |
|
448 // Determine whether long or short form is used |
|
449 if (iIs16Bit || iSourcePort > 255 || iDestinationPort>255) |
|
450 { |
|
451 elemIndex = 0; |
|
452 aFixedHeader.Copy(KSCKHeaderLong); |
|
453 } |
|
454 else |
|
455 { |
|
456 elemIndex = 3; |
|
457 aFixedHeader.Copy(KSCKHeaderShort); |
|
458 } |
|
459 |
|
460 // Set destination port |
|
461 hexNumber.NumFixedWidthUC(iDestinationPort,EHex, |
|
462 KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]); |
|
463 aFixedHeader.Append(hexNumber); |
|
464 |
|
465 // Don't set the source port, |
|
466 // 1) if it is not set or it same as destination port and |
|
467 // 2) data (and other header) fits in one segment |
|
468 if (!((iSourcePort==-1 || iDestinationPort==iSourcePort) |
|
469 && CalculateTotalSegments(KElemIndexes[elemIndex].iIndexes[minorIndex+1])==1)) |
|
470 { |
|
471 // Source port is present |
|
472 elemIndex++; |
|
473 minorIndex++; |
|
474 if (iSourcePort==-1) |
|
475 iSourcePort = iDestinationPort; |
|
476 |
|
477 // Set source port |
|
478 hexNumber.NumFixedWidthUC(iSourcePort,EHex, |
|
479 KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]); |
|
480 aFixedHeader.Append(hexNumber); |
|
481 |
|
482 // Add the SAR info when source port is set |
|
483 elemIndex++; |
|
484 |
|
485 // Set reference |
|
486 minorIndex++; |
|
487 hexNumber.NumFixedWidthUC(iReference,EHex, |
|
488 KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]); |
|
489 aFixedHeader.Append(hexNumber); |
|
490 |
|
491 // Set fragment count |
|
492 minorIndex++; |
|
493 CalculateTotalSegments(KElemIndexes[elemIndex].iIndexes[KElemIndexes[elemIndex].iLastIndex]); |
|
494 hexNumber.NumFixedWidthUC(iTotalSegments,EHex, |
|
495 KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]); |
|
496 aFixedHeader.Append(hexNumber); |
|
497 |
|
498 // Return the index for segment number in the header |
|
499 minorIndex++; |
|
500 aSegmentNumberIndex = KElemIndexes[elemIndex].iIndexes[minorIndex]; |
|
501 segmentNumberLength = 2; |
|
502 } |
|
503 |
|
504 aFixedHeader.Append(iRefOtherHeader); |
|
505 aFixedHeader.Append(_L(" ")); |
|
506 |
|
507 return aFixedHeader.Length()+segmentNumberLength; |
|
508 } // TWapTextMessage::CreateHeader |
|
509 |
|
510 |
|
511 /** |
|
512 * Calculates count of segments to send a used data (based on iRefData) |
|
513 * The values is assigned to iTotalSegments |
|
514 * The affect of terminating ' ' is taken into inside the method |
|
515 */ |
|
516 TInt TWapTextMessage::CalculateTotalSegments(TInt aFixedLength) |
|
517 { |
|
518 LOGWAPPROT2("TWapTextMessage::CalculateTotalSegments [aFixedLength=%d]", aFixedLength); |
|
519 |
|
520 // '+1': length of terminating ' ' |
|
521 TInt length = aFixedLength + iRefOtherHeader.Length() + 1; |
|
522 TInt remain = KMaxSmsChars - length; |
|
523 TInt dataSegmented = 0; |
|
524 |
|
525 __ASSERT_DEBUG(remain > 0, Panic(KPanicTextHeaderTooLong)); |
|
526 |
|
527 iTotalSegments = 0; |
|
528 do |
|
529 { |
|
530 iTotalSegments++; |
|
531 |
|
532 // |
|
533 // Count any escaped characters we can be sure that the converted data |
|
534 // size fits inside the remaining length (e.g. so that non-7bit characters |
|
535 // when converted by the SMS Stack will still fit). |
|
536 // |
|
537 TInt segmentSize = iRefData.Length() - dataSegmented; |
|
538 |
|
539 if (segmentSize > remain) |
|
540 { |
|
541 segmentSize = remain; |
|
542 } |
|
543 |
|
544 while (segmentSize > 1) |
|
545 { |
|
546 TPtrC8 segmentData(iRefData.Mid(dataSegmented, segmentSize)); |
|
547 TInt non7bitCharEscapes = 0; |
|
548 |
|
549 // |
|
550 // Count all non-7bit characters that will be escaped (many non-7bit |
|
551 // characters are not escaped, but converted to "?"). The ones |
|
552 // that are known to be escaped are list below: |
|
553 // |
|
554 // 12 [Form Feed]. |
|
555 // 91 "[" |
|
556 // 92 "\" |
|
557 // 93 "]" |
|
558 // 94 "^" |
|
559 // 123 "{" |
|
560 // 124 "|" |
|
561 // 125 "}" |
|
562 // 126 "~" |
|
563 // |
|
564 for (TInt ch = 0; ch < segmentSize; ch++) |
|
565 { |
|
566 if (segmentData[ch] == 12 || |
|
567 (segmentData[ch] >= 91 && segmentData[ch] <= 94) || |
|
568 (segmentData[ch] >= 123 && segmentData[ch] <= 126)) |
|
569 { |
|
570 non7bitCharEscapes++; |
|
571 } |
|
572 } |
|
573 |
|
574 // |
|
575 // Can it fit? If so store it, otherwise reduce the size... |
|
576 // |
|
577 if (segmentData.Length() + non7bitCharEscapes <= remain) |
|
578 { |
|
579 break; |
|
580 } |
|
581 |
|
582 segmentSize--; |
|
583 } |
|
584 |
|
585 dataSegmented += segmentSize; |
|
586 } |
|
587 while (dataSegmented < iRefData.Length()); |
|
588 |
|
589 // |
|
590 // At least one fragment is needed... |
|
591 // |
|
592 if (iTotalSegments == 0) |
|
593 { |
|
594 iTotalSegments = 1; |
|
595 } |
|
596 |
|
597 return iTotalSegments; |
|
598 } // TWapTextMessage::CalculateTotalSegments |
|
599 |
|
600 // EOF - WAPTHDR.CPP |