1 /*
2  *
3  * Inter Asterisk Exchange 2
4  *
5  * Implementation of the class that specifies frame construction.
6  *
7  * Open Phone Abstraction Library (OPAL)
8  *
9  * Copyright (c) 2005 Indranet Technologies Ltd.
10  *
11  * The contents of this file are subject to the Mozilla Public License
12  * Version 1.0 (the "License"); you may not use this file except in
13  * compliance with the License. You may obtain a copy of the License at
14  * http://www.mozilla.org/MPL/
15  *
16  * Software distributed under the License is distributed on an "AS IS"
17  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
18  * the License for the specific language governing rights and limitations
19  * under the License.
20  *
21  * The Original Code is Open Phone Abstraction Library.
22  *
23  * The Initial Developer of the Original Code is Indranet Technologies Ltd.
24  *
25  * The author of this code is Derek J Smithies
26  *
27  * $Revision: 24607 $
28  * $Author: dereksmithies $
29  * $Date: 2010-07-28 22:53:29 -0500 (Wed, 28 Jul 2010) $
30  */
31 
32 #include <ptlib.h>
33 #include <opal/buildopts.h>
34 
35 #if OPAL_IAX2
36 
37 #ifdef P_USE_PRAGMA
38 #pragma implementation "frame.h"
39 #endif
40 
41 #include <iax2/frame.h>
42 
43 #include <iax2/iax2con.h>
44 #include <iax2/iax2ep.h>
45 #include <iax2/ies.h>
46 #include <iax2/receiver.h>
47 #include <iax2/transmit.h>
48 
49 #include <ptclib/cypher.h>
50 
51 #if OPAL_PTLIB_SSL_AES
52 #include <openssl/aes.h>
53   #ifdef _MSC_VER
54     #pragma comment(lib, P_SSL_LIB1)
55     #pragma comment(lib, P_SSL_LIB2)
56   #endif
57 #endif
58 
59 
60 #define new PNEW
61 
62 
IAX2Frame(IAX2EndPoint & _endpoint)63 IAX2Frame::IAX2Frame(IAX2EndPoint &_endpoint)
64   : endpoint(_endpoint)
65 {
66   ZeroAllValues();
67 
68   PTRACE(6, "Frame\tConstruct IAX2Frame  " << IdString());
69 }
70 
~IAX2Frame()71 IAX2Frame::~IAX2Frame()
72 {
73   PTRACE(6, "Frame\tDestructor for IAX2Frame  " << *this);
74 }
75 
IdString() const76 PString IAX2Frame::IdString() const
77 {
78   PStringStream answer;
79   answer << PString("FR-ID#") << ::hex << this << ::dec;
80   return answer;
81 }
82 
ZeroAllValues()83 void IAX2Frame::ZeroAllValues()
84 {
85   data.SetSize(0);
86 
87   isFullFrame       = PFalse;
88   isVideo           = PFalse;
89   isAudio           = PFalse;
90 
91   currentReadIndex  = 0;
92   currentWriteIndex = 0;
93   timeStamp = 0;
94 
95   canRetransmitFrame = PFalse;
96   presetTimeStamp = 0;
97 
98   frameType = undefType;
99 }
100 
101 
SetTimeStamp(DWORD newValue)102 void IAX2Frame::SetTimeStamp(DWORD newValue)
103 {
104   timeStamp  = newValue;
105   presetTimeStamp = newValue;
106   PTRACE(5, "Frame\tPreset the timestamp to " << newValue);
107 }
108 
ReadNetworkPacket(PUDPSocket & sock)109 PBoolean IAX2Frame::ReadNetworkPacket(PUDPSocket &sock)
110 {
111   data.SetSize(4096);  //Surely no packets > 4096 bytes in length
112 
113   WORD     portNo;
114   PIPSocket::Address addr;
115   sock.GetLocalAddress(addr);
116 
117   PBoolean res = sock.ReadFrom(data.GetPointer(), 4096, addr, portNo);
118   remote.SetRemoteAddress(addr);
119   remote.SetRemotePort(portNo);
120 
121   if (res == PFalse) {
122     PTRACE(3, "Frame\tFailed in reading from socket");
123     return PFalse;
124   }
125 
126   data.SetSize(sock.GetLastReadCount());
127 
128   if (data.GetSize() < 4) {
129     PTRACE(3, "Frame\tRead a very very small packet from the network - < 4 bytes");
130     return PFalse;
131   }
132 
133   return PTrue;
134 }
135 
Read1Byte(BYTE & result)136 PBoolean IAX2Frame::Read1Byte(BYTE & result)
137 {
138   if (currentReadIndex >= data.GetSize())
139     return PFalse;
140 
141   result = data[currentReadIndex];
142   currentReadIndex++;
143   return PTrue;
144 }
145 
Read2Bytes(PINDEX & res)146 PBoolean IAX2Frame::Read2Bytes(PINDEX & res)
147 {
148   BYTE a = 0;
149   BYTE b = 0;
150   if (Read1Byte(a) && Read1Byte(b)) {
151     res = (a << 8) | b;
152     return PTrue;
153   }
154 
155   return PFalse;
156 }
157 
Read2Bytes(WORD & res)158 PBoolean IAX2Frame::Read2Bytes(WORD & res)
159 {
160   BYTE a = 0, b = 0;
161   if (Read1Byte(a) && Read1Byte(b)) {
162     res = (WORD)((a << 8) | b);
163     return PTrue;
164   }
165 
166   return PFalse;
167 }
168 
Read4Bytes(DWORD & res)169 PBoolean IAX2Frame::Read4Bytes(DWORD & res)
170 {
171   PINDEX a = 0, b = 0;
172   if (Read2Bytes(a) && Read2Bytes(b)) {
173     res = (a << 16) | b;
174     return PTrue;
175   }
176 
177   return PFalse;
178 }
179 
Write1Byte(PINDEX newVal)180 void IAX2Frame::Write1Byte(PINDEX newVal)
181 {
182   Write1Byte((BYTE)(newVal & 0xff));
183 }
184 
Write1Byte(BYTE newVal)185 void IAX2Frame::Write1Byte(BYTE newVal)
186 {
187   if (currentWriteIndex >= data.GetSize())
188     data.SetSize(currentWriteIndex + 1);
189 
190   data[currentWriteIndex] = newVal;
191   currentWriteIndex++;
192 }
193 
Write2Bytes(PINDEX newVal)194 void IAX2Frame::Write2Bytes(PINDEX newVal)
195 {
196   BYTE a = (BYTE)(newVal >> 8);
197   BYTE b = (BYTE)(newVal & 0xff);
198 
199   Write1Byte(a);
200   Write1Byte(b);
201 }
202 
Write4Bytes(unsigned int newVal)203 void IAX2Frame::Write4Bytes(unsigned int newVal)
204 {
205   PINDEX a = newVal >> 16;
206   PINDEX b = newVal & 0xffff;
207 
208   Write2Bytes(a);
209   Write2Bytes(b);
210 }
211 
212 
ProcessNetworkPacket()213 PBoolean IAX2Frame::ProcessNetworkPacket()
214 {
215   /*We are guaranteed to have a packet > 4 bytes in size */
216   PINDEX a = 0;
217   Read2Bytes(a);
218   PINDEX sourceCallNumber = a & 0x7fff;
219   remote.SetSourceCallNumber(sourceCallNumber);
220 
221   if (sourceCallNumber > 1)
222     /*Call token frames have a source call number of 1 */
223     BuildConnectionToken();
224 
225   if (a & 0x8000) {
226     isFullFrame = PTrue;
227     Read2Bytes(a);
228     remote.SetDestCallNumber(a & 0x7fff);
229     return PTrue;
230   }
231   if (a == 0) {    //We have a mini frame here, of video type.
232     isVideo = PTrue;
233     PINDEX b = 0;
234     Read2Bytes(b);
235     remote.SetSourceCallNumber(b);
236     BuildConnectionToken();
237     return PTrue;
238   }
239 
240   isAudio = PTrue;
241   return PTrue;
242 }
243 
BuildConnectionToken()244 void IAX2Frame::BuildConnectionToken()
245 {
246   connectionToken = remote.BuildConnectionToken();
247 }
248 
PrintOn(ostream & strm) const249 void IAX2Frame::PrintOn(ostream & strm) const
250 {
251   strm << IdString() << "      " << data.GetSize() << " bytes " << endl;
252 }
253 
BuildTimeStamp(const PTimeInterval & callStartTick)254 void IAX2Frame::BuildTimeStamp(const PTimeInterval & callStartTick)
255 {
256   if (presetTimeStamp > 0)
257     timeStamp = presetTimeStamp;
258   else
259     timeStamp = CalcTimeStamp(callStartTick);
260   PTRACE(5, "Frame\tBuild time stamp to " << PString(timeStamp) << " ms");
261 }
262 
CalcTimeStamp(const PTimeInterval & callStartTick)263 DWORD IAX2Frame::CalcTimeStamp(const PTimeInterval & callStartTick)
264 {
265   DWORD tVal = (DWORD)(PTimer::Tick() - callStartTick).GetMilliSeconds();
266   PTRACE(6, "Frame\tCalculate timestamp as " << tVal);
267   return tVal;
268 }
269 
TransmitPacket(PUDPSocket & sock)270 PBoolean IAX2Frame::TransmitPacket(PUDPSocket &sock)
271 {
272   if (CallMustBeActive()) {
273     if (!endpoint.ConnectionForFrameIsAlive(this)) {
274       PTRACE(3, "Frame\tConnection not found, call has been terminated. "
275 	     << IdString());
276       return PFalse;   //This happens because the call has been terminated.
277     }
278   }
279 
280   //     if (PIsDescendant(this, FullFrameProtocol))
281   //   	    ((FullFrameProtocol *)this)->SetRetransmissionRequired();
282   //	    cout << endl;
283 
284   PTRACE(6, "Frame\tNow transmit " << endl << *this);
285   PBoolean transmitResult = sock.WriteTo(data.GetPointer(), DataSize(), remote.RemoteAddress(),
286 				     (unsigned short)remote.RemotePort());
287   PTRACE(6, "Frame\ttransmission of packet gave a " << transmitResult);
288   return transmitResult;
289 }
290 
BuildAppropriateFrameType(IAX2Encryption & encryptionInfo)291 IAX2Frame * IAX2Frame::BuildAppropriateFrameType(IAX2Encryption & encryptionInfo)
292 {
293   DecryptContents(encryptionInfo);
294 
295   return BuildAppropriateFrameType();
296 }
297 
BuildAppropriateFrameType()298 IAX2Frame *IAX2Frame::BuildAppropriateFrameType()
299 {
300   if (isFullFrame) {
301     IAX2FullFrame *ff = new IAX2FullFrame(*this);
302     if (!ff->ProcessNetworkPacket()) {
303       delete ff;
304       return NULL;
305     }
306 
307     return ff;
308   }
309 
310   IAX2MiniFrame *mf = new IAX2MiniFrame(*this);
311   if (!mf->ProcessNetworkPacket()) {
312     delete mf;
313     return NULL;
314   }
315 
316   return mf;
317 }
318 
319 
DecryptContents(IAX2Encryption & encryption)320 PBoolean IAX2Frame::DecryptContents(IAX2Encryption &encryption)
321 {
322   if (!encryption.IsEncrypted())
323     return PTrue;
324 
325 #if OPAL_PTLIB_SSL_AES
326   PINDEX headerSize = GetEncryptionOffset();
327   PTRACE(4, "Frame\tUnEncrypted headerSize for " << IdString() << " is " << headerSize);
328 
329   if ((headerSize + 32) > data.GetSize())         //Make certain packet larger than minimum size.
330     return PFalse;
331 
332   PTRACE(6, "Decryption\tDATA Raw is " << endl << ::hex << data << ::dec);
333   PINDEX encDataSize = data.GetSize() - headerSize;
334   PTRACE(4, "Decryption\tEncoded data size is " << encDataSize);
335   if ((encDataSize % 16) != 0) {
336     PTRACE(2, "Decryption\tData size is not a multiple of 16.. Error. ");
337     return PFalse;
338   }
339 
340   unsigned char lastblock[16];
341   memset(lastblock, 0, 16);
342   PBYTEArray working(encDataSize);
343 
344   for (PINDEX i = 0; i < encDataSize; i+= 16) {
345     AES_decrypt(data.GetPointer() + headerSize + i, working.GetPointer() + i, encryption.AesDecryptKey());
346     for (int x = 0; x < 16; x++)
347       working[x + i] ^= lastblock[x];
348     memcpy(lastblock, data.GetPointer() + headerSize + i, 16);
349   }
350 
351   PINDEX padding = 16 + (working[15] & 0x0f);
352   PTRACE(6, "padding is " << padding);
353 
354   PINDEX encryptedSize = encDataSize - padding;
355   data.SetSize(encryptedSize + headerSize);
356 
357   PTRACE(6, "Decryption\tDATA should have a size of " << data.GetSize());
358   PTRACE(6, "Decryption\tUNENCRYPTED DATA is " << endl << ::hex << working << ::dec);
359 
360   memcpy(data.GetPointer() + headerSize, working.GetPointer() + padding, encryptedSize);
361   PTRACE(6, "Decryption\tEntire frame unencrypted is " << endl << ::hex << data << ::dec);
362   return PTrue;
363 #else
364   return PFalse;
365 #endif
366 }
367 
EncryptContents(IAX2Encryption & encryption)368 PBoolean IAX2Frame::EncryptContents(IAX2Encryption &encryption)
369 {
370   if (!encryption.IsEncrypted())
371     return PTrue;
372 
373 #if OPAL_PTLIB_SSL_AES
374   PINDEX headerSize = GetEncryptionOffset();
375   PINDEX eDataSize = data.GetSize() - headerSize;
376   PINDEX padding = 16 + ((16 - (eDataSize % 16)) & 0x0f);
377   PTRACE(6, "Frame\tEncryption, Size of encrypted region is changed from "
378 	 << eDataSize << "  to " << (padding + eDataSize));
379 
380   PBYTEArray working(eDataSize + padding);
381   memset(working.GetPointer(), 0, 16);
382   working[15] = (BYTE)(0x0f & padding);
383   memcpy(working.GetPointer() + padding, data.GetPointer() + headerSize, eDataSize);
384 
385   PBYTEArray result(headerSize + eDataSize + padding);
386   memcpy(result.GetPointer(), data.GetPointer(), headerSize);
387 
388   unsigned char curblock[16];
389   memset(curblock, 0, 16);
390   for (PINDEX i = 0; i < (eDataSize + padding); i+= 16) {
391     for (int x = 0; x < 16; x++)
392       curblock[x] ^= working[x + i];
393     AES_encrypt(curblock, result.GetPointer() + i + headerSize, encryption.AesEncryptKey());
394     memcpy(curblock, result.GetPointer() + i + headerSize, 16);
395   }
396 
397   data = result;
398   return PTrue;
399 #else
400   PTRACE(1, "Frame\tEncryption is Flagged on, but AES routines in openssl are not available");
401   return PFalse;
402 #endif
403 }
404 
GetEncryptionOffset()405 PINDEX IAX2Frame::GetEncryptionOffset()
406 {
407   if (isFullFrame)
408     return 4;
409 
410   return 2;
411 }
412 
413 ////////////////////////////////////////////////////////////////////////////////
414 
IAX2MiniFrame(const IAX2Frame & srcFrame)415 IAX2MiniFrame::IAX2MiniFrame(const IAX2Frame & srcFrame)
416   : IAX2Frame(srcFrame)
417 {
418   ZeroAllValues();
419   isAudio = (data[0] != 0) || (data[1] != 0);
420   isVideo = !isAudio;
421   PTRACE(6, "Build this IAX2MiniFrame " << IdString());
422 }
423 
IAX2MiniFrame(IAX2EndPoint & _endpoint)424 IAX2MiniFrame::IAX2MiniFrame(IAX2EndPoint &_endpoint)
425   : IAX2Frame(_endpoint)
426 {
427   ZeroAllValues();
428   PTRACE(6, "Build this IAX2MiniFrame " << IdString());
429 }
430 
IAX2MiniFrame(IAX2Processor * iax2Processor,PBYTEArray & sound,PBoolean _isAudio,DWORD usersTimeStamp)431 IAX2MiniFrame::IAX2MiniFrame(IAX2Processor * iax2Processor, PBYTEArray &sound,
432 		     PBoolean _isAudio, DWORD usersTimeStamp)
433   : IAX2Frame(iax2Processor->GetEndPoint())
434 {
435   isAudio = _isAudio;
436   presetTimeStamp = usersTimeStamp;
437   InitialiseHeader(iax2Processor);
438 
439   PINDEX headerSize = data.GetSize();
440   data.SetSize(sound.GetSize() + headerSize);
441   memcpy(data.GetPointer() + headerSize, sound.GetPointer(), sound.GetSize());
442   PTRACE(6, "Build this IAX2MiniFrame " << IdString());
443 }
444 
~IAX2MiniFrame()445 IAX2MiniFrame::~IAX2MiniFrame()
446 {
447   PTRACE(6, "Destroy this IAX2MiniFrame " << IdString());
448 }
449 
InitialiseHeader(IAX2Processor * iax2Processor)450 void IAX2MiniFrame::InitialiseHeader(IAX2Processor *iax2Processor)
451 {
452   if (iax2Processor != NULL) {
453     remote   = iax2Processor->GetRemoteInfo();
454     BuildTimeStamp(iax2Processor->GetCallStartTick());
455     SetConnectionToken(iax2Processor->GetCallToken());
456   }
457   WriteHeader();
458 }
459 
ProcessNetworkPacket()460 PBoolean IAX2MiniFrame::ProcessNetworkPacket()
461 {
462   WORD dataWord;
463   Read2Bytes(dataWord);
464   timeStamp = dataWord;
465 
466   PTRACE(5, "Mini frame, header processed. frame is "
467 	 << PString(isAudio ? " audio" : "video"));
468   return PTrue;
469 }
470 
ZeroAllValues()471 void IAX2MiniFrame::ZeroAllValues()
472 {
473 }
474 
AlterTimeStamp(PINDEX newValue)475 void IAX2MiniFrame::AlterTimeStamp(PINDEX newValue)
476 {
477   timeStamp = (newValue & (0xffff << 16)) | (timeStamp & 0xffff);
478 }
479 
WriteHeader()480 PBoolean IAX2MiniFrame::WriteHeader()
481 {
482   currentWriteIndex = 0;   //Probably not needed, but this makes everything "obvious"
483 
484   if(IsVideo()) {
485     data.SetSize(6);
486     Write2Bytes(0);
487   } else
488     data.SetSize(4);
489 
490   Write2Bytes(remote.SourceCallNumber() & 0x7fff);
491   Write2Bytes(timeStamp & 0xffff);
492 
493   return PTrue;
494 }
495 
PrintOn(ostream & strm) const496 void IAX2MiniFrame::PrintOn(ostream & strm) const
497 {
498   strm << "IAX2MiniFrame of " << PString(IsVideo() ? "video" : "audio") << " "
499        << IdString() << " \"" << GetConnectionToken() << "\"  " << endl;
500 
501   IAX2Frame::PrintOn(strm);
502 }
503 
GetMediaDataPointer()504 BYTE *IAX2MiniFrame::GetMediaDataPointer()
505 {
506   if (IsVideo())
507     return data.GetPointer() + 6;
508   else
509     return data.GetPointer() + 4;
510 }
511 
GetMediaDataSize()512 PINDEX IAX2MiniFrame::GetMediaDataSize()
513 {
514   int thisSize;
515   if (IsVideo())
516     thisSize = data.GetSize() - 6;
517   else
518     thisSize = data.GetSize() - 4;
519 
520   if (thisSize < 0)
521     return 0;
522   else
523     return thisSize;
524 }
525 
GetEncryptionOffset()526 PINDEX IAX2MiniFrame::GetEncryptionOffset()
527 {
528   if (IsAudio())
529     return 2;
530 
531   return 4;
532 }
533 
534 ////////////////////////////////////////////////////////////////////////////////
535 
IAX2FullFrame(IAX2EndPoint & _newEndpoint)536 IAX2FullFrame::IAX2FullFrame(IAX2EndPoint &_newEndpoint)
537   : IAX2Frame(_newEndpoint)
538 {
539   ZeroAllValues();
540 }
541 
542 /*This is built from an incoming frame (from the network*/
IAX2FullFrame(const IAX2Frame & srcFrame)543 IAX2FullFrame::IAX2FullFrame(const IAX2Frame & srcFrame)
544   : IAX2Frame(srcFrame)
545 {
546   PTRACE(6, "Frame\tConstructor for a full frame");
547   ZeroAllValues();
548 }
549 
~IAX2FullFrame()550 IAX2FullFrame::~IAX2FullFrame()
551 {
552   PTRACE(6, "Frame\tDestructor IAX2FullFrame:: " << IdString());
553 }
554 
operator *=(IAX2FullFrame &)555 PBoolean IAX2FullFrame::operator*=(IAX2FullFrame & /*other*/)
556 {
557   PAssertAlways("Sorry, IAX2FullFrame comparison operator is Not implemented");
558   return PTrue;
559 }
560 
MarkAsResent()561 void IAX2FullFrame::MarkAsResent()
562 {
563   if (data.GetSize() > 2)
564     data[2] |= 0x80;
565 }
566 
ZeroAllValues()567 void IAX2FullFrame::ZeroAllValues()
568 {
569   subClass = 0;
570   timeStamp = 0;
571   sequence.ZeroAllValues();
572   canRetransmitFrame = PTrue;
573 
574   transmissionTimer.SetNotifier(PCREATE_NOTIFIER(OnTransmissionTimeout));
575 
576   retryDelta = PTimeInterval(minRetryTime);
577   retries = maxRetries;
578 
579   callMustBeActive = PTrue;
580   packetResent = PFalse;
581   ClearListFlags();
582 
583   isFullFrame = PTrue;
584   isAckFrame = PFalse;
585 }
586 
ClearListFlags()587 void IAX2FullFrame::ClearListFlags()
588 {
589   deleteFrameNow = PFalse;
590   sendFrameNow   = PFalse;
591 }
592 
TransmitPacket(PUDPSocket & sock)593 PBoolean IAX2FullFrame::TransmitPacket(PUDPSocket &sock)
594 {
595   PTRACE(6, "Send network packet on " << IdString() << " " << connectionToken);
596   if (packetResent) {
597     MarkAsResent();      /* Set Retry flag.*/
598   }
599 
600   if (retries == P_MAX_INDEX) {
601     PTRACE(4, "Retries count is now negative on. " << IdString());
602     return PFalse;    //Give up on this packet, it has exceeded the allowed number of retries.
603   }
604 
605   PTRACE(6, "Start timer running for " << IdString() << connectionToken);
606   transmissionTimer.SetInterval(retryDelta.GetMilliSeconds());
607   transmissionTimer.Reset();   //*This causes the timer to start running.
608   ClearListFlags();
609 
610   return IAX2Frame::TransmitPacket(sock);
611 }
612 
ProcessNetworkPacket()613 PBoolean IAX2FullFrame::ProcessNetworkPacket()
614 {
615   PTRACE(5, "ProcessNetworkPacket - read the frame header");
616   if (data.GetSize() < 12) {
617     PTRACE(2, "Incoming full frame is undersize - should have 12 bytes, but only read " << data.GetSize());
618     return PFalse;
619   }
620 
621   Read4Bytes(timeStamp);
622   PTRACE(5, "Remote timestamp is " << timeStamp  << " milliseconds");
623 
624   BYTE a = 0;
625   Read1Byte(a);
626   sequence.SetOutSeqNo(a);
627   Read1Byte(a);
628   sequence.SetInSeqNo(a);
629   PTRACE(6, "Sequence is " << sequence.AsString());
630 
631   Read1Byte(a);
632 
633   if ((a >= numFrameTypes) || (a == undefType)) {
634     PTRACE(3, "Incoming packet has invalid frame type of " << a);
635     return PFalse;
636   }
637 
638   frameType = (IAX2FrameType)a;
639   isAudio = frameType == voiceType;
640   isVideo = frameType == videoType;
641 
642   /*Do subclass value (which might be compressed) */
643   Read1Byte(a);
644 
645   UnCompressSubClass(a);
646   isAckFrame = (subClass == IAX2FullFrameProtocol::cmdAck) &&
647     (frameType == iax2ProtocolType);
648   return PTrue;
649 }
650 
IsPingFrame()651 PBoolean IAX2FullFrame::IsPingFrame()
652 {
653   return (subClass == IAX2FullFrameProtocol::cmdPing) &&
654     (frameType == iax2ProtocolType);
655 }
656 
IsNewFrame()657 PBoolean IAX2FullFrame::IsNewFrame()
658 {
659   return (subClass == IAX2FullFrameProtocol::cmdNew) &&
660     (frameType == iax2ProtocolType);
661 }
662 
IsLagRqFrame()663 PBoolean IAX2FullFrame::IsLagRqFrame()
664 {
665   return (subClass == IAX2FullFrameProtocol::cmdLagRq) &&
666     (frameType == iax2ProtocolType);
667 }
668 
IsLagRpFrame()669 PBoolean IAX2FullFrame::IsLagRpFrame()
670 {
671   return (subClass == IAX2FullFrameProtocol::cmdLagRp) &&
672     (frameType == iax2ProtocolType);
673 }
674 
IsPongFrame()675 PBoolean IAX2FullFrame::IsPongFrame()
676 {
677   return (subClass == IAX2FullFrameProtocol::cmdPong) &&
678     (frameType == iax2ProtocolType);
679 }
680 
IsAuthReqFrame()681 PBoolean IAX2FullFrame::IsAuthReqFrame()
682 {
683   return (subClass == IAX2FullFrameProtocol::cmdAuthReq) &&
684     (frameType == iax2ProtocolType);
685 }
686 
IsVnakFrame()687 PBoolean IAX2FullFrame::IsVnakFrame()
688 {
689   return (subClass == IAX2FullFrameProtocol::cmdVnak) &&
690     (frameType == iax2ProtocolType);
691 }
692 
IsRegReqFrame()693 PBoolean IAX2FullFrame::IsRegReqFrame()
694 {
695   return (subClass == IAX2FullFrameProtocol::cmdRegReq) &&
696     (frameType == iax2ProtocolType);
697 }
698 
IsRegAuthFrame()699 PBoolean IAX2FullFrame::IsRegAuthFrame()
700 {
701   return (subClass == IAX2FullFrameProtocol::cmdRegAuth) &&
702     (frameType == iax2ProtocolType);
703 }
704 
IsRegAckFrame()705 PBoolean IAX2FullFrame::IsRegAckFrame()
706 {
707   return (subClass == IAX2FullFrameProtocol::cmdRegAck) &&
708     (frameType == iax2ProtocolType);
709 }
710 
IsRegRelFrame()711 PBoolean IAX2FullFrame::IsRegRelFrame()
712 {
713   return (subClass == IAX2FullFrameProtocol::cmdRegRel) &&
714     (frameType == iax2ProtocolType);
715 }
716 
IsRegRejFrame()717 PBoolean IAX2FullFrame::IsRegRejFrame()
718 {
719   return (subClass == IAX2FullFrameProtocol::cmdRegRej) &&
720     (frameType == iax2ProtocolType);
721 }
722 
IsHangupFrame()723 PBoolean IAX2FullFrame::IsHangupFrame()
724 {
725   return (subClass == IAX2FullFrameProtocol::cmdHangup) &&
726     (frameType == iax2ProtocolType);
727 }
728 
IsCallTokenFrame()729 PBoolean IAX2FullFrame::IsCallTokenFrame()
730 {
731   return (subClass == IAX2FullFrameProtocol::cmdCallToken) &&
732     (frameType == iax2ProtocolType);
733 }
734 
FrameIncrementsInSeqNo()735 PBoolean IAX2FullFrame::FrameIncrementsInSeqNo()
736 {
737   if (frameType != iax2ProtocolType) {
738     PTRACE(5, "SeqNos\tFrameType is not iaxProtocol, so we do increment inseqno. FrameType is " << frameType);
739     return PTrue;
740   }
741 
742   IAX2FullFrameProtocol::ProtocolSc cmdType = (IAX2FullFrameProtocol::ProtocolSc)subClass;
743   PTRACE(5, "SeqNos\tThe cmd type (or subclass of IAX2FullFrameProtocol) is " << cmdType);
744   if ((cmdType == IAX2FullFrameProtocol::cmdAck)     ||
745       //  (cmdType == IAX2FullFrameProtocol::cmdLagRq)   ||
746       //  (cmdType == IAX2FullFrameProtocol::cmdLagRp)   ||
747       //  (cmdType == IAX2FullFrameProtocol::cmdAuthReq) ||
748       //  (cmdType == IAX2FullFrameProtocol::cmdAuthRep) ||
749       (cmdType == IAX2FullFrameProtocol::cmdVnak))   {
750     PTRACE(3, "SeqNos\tThis is a iaxProtocol cmd type that does not increment inseqno");
751     return PFalse;
752   } else {
753     PTRACE(5, "SeqNos\tThis is a iaxProtocol cmd type that increments inseqno");
754   }
755   return PTrue;
756 }
757 
UnCompressSubClass(BYTE a)758 void IAX2FullFrame::UnCompressSubClass(BYTE a)
759 {
760   if (a & 0x80) {
761     if (a == 0xff)
762       subClass = -1;
763     else
764       subClass = 1 << (a & 0x1f);
765   } else
766     subClass = a;
767 }
768 
CompressSubClass()769 int IAX2FullFrame::CompressSubClass()
770 {
771   if (subClass < 0x80)
772     return subClass;
773 
774   for(PINDEX i = 0; i <  0x1f; i++) {
775     if (subClass & (1 << i))
776       return i | 0x80;
777   }
778 
779   return -1;
780 }
781 
WriteHeader()782 PBoolean IAX2FullFrame::WriteHeader()
783 {
784   data.SetSize(12);
785   PTRACE(6, "Write a source call number of " << remote.SourceCallNumber());
786   Write2Bytes(remote.SourceCallNumber() + 0x8000);
787   PTRACE(6, "Write a dest call number of " << remote.DestCallNumber());
788   Write2Bytes(remote.DestCallNumber() + (packetResent ? 0x8000 : 0));
789 
790   PTRACE(6, "Write a timestamp of " << timeStamp);
791   Write4Bytes(timeStamp);
792 
793   PTRACE(6, "Write in seq no " << sequence.InSeqNo() << " and out seq no of " << sequence.OutSeqNo());
794   Write1Byte(sequence.OutSeqNo());
795   Write1Byte(sequence.InSeqNo());
796 
797   PTRACE(6, "FrameType is " << ((int)GetFullFrameType()));
798   Write1Byte(GetFullFrameType());
799 
800   int a = CompressSubClass();
801   if (a < 0)
802     Write1Byte(0xff);
803   else
804     Write1Byte((BYTE)a);
805   PTRACE(6, "Comppressed sub class is " << a << " from " << subClass);
806 
807   return PTrue;
808 }
809 
MarkVnakSendNow()810 void IAX2FullFrame::MarkVnakSendNow()
811 {
812   transmissionTimer.Stop();
813   sendFrameNow = PTrue;
814   deleteFrameNow = PFalse;
815   retryDelta = PTimeInterval(minRetryTime);
816   retries = maxRetries;
817 }
818 
MarkDeleteNow()819 void IAX2FullFrame::MarkDeleteNow()
820 {
821   PTRACE(5, "MarkDeleteNow() method on " << IdString());
822   transmissionTimer.Stop();
823   deleteFrameNow = PTrue;
824   retries = P_MAX_INDEX;
825 }
826 
OnTransmissionTimeout(PTimer &,INT)827 void IAX2FullFrame::OnTransmissionTimeout(PTimer &, INT)
828 {
829   PTRACE(4, "Has had a TX timeout " << IdString() << " " << connectionToken);
830   retryDelta = 4 * retryDelta.GetMilliSeconds();
831   if (retryDelta > maxRetryTime)
832     retryDelta = maxRetryTime;
833 
834   packetResent = PTrue;
835   if ((retries == P_MAX_INDEX) || (retries == 0)) {
836     retries = P_MAX_INDEX;
837     PTRACE(5, "Retries are " << PString(retries)
838 	   << " NowMarkDeleteNow " << IdString());
839     MarkDeleteNow();
840   } else {
841     retries--;
842     sendFrameNow = PTrue;
843     PTRACE(5, "Tx timeout, so Mark as Send now " << IdString() << " " << connectionToken);
844   }
845 
846   endpoint.transmitter->ProcessLists();
847 }
848 
GetFullFrameName() const849 PString IAX2FullFrame::GetFullFrameName() const
850 {
851   switch(frameType)  {
852       case undefType       : return PString("(0?)      ");
853       case dtmfType        : return PString("Dtmf      ");
854       case voiceType       : return PString("Voice     ");
855       case videoType       : return PString("Video     ");
856       case controlType     : return PString("Session   ");
857       case nullType        : return PString("Null      ");
858       case iax2ProtocolType: return PString("Protocol  ");
859       case textType        : return PString("Text      ");
860       case imageType       : return PString("Image     ");
861       case htmlType        : return PString("Html      ");
862       case cngType         : return PString("Cng       ");
863       case numFrameTypes   : return PString("# F types ");
864   }
865 
866   return PString("Frame name is undefined for value of ") + PString(frameType);
867 }
868 
GetMediaDataPointer()869 BYTE *IAX2FullFrame::GetMediaDataPointer()
870 {
871   return data.GetPointer() + 12;
872 }
873 
GetMediaDataSize()874 PINDEX IAX2FullFrame::GetMediaDataSize()
875 {
876   return data.GetSize() - 12;
877 }
878 
InitialiseHeader(IAX2Processor * iax2Processor)879 void IAX2FullFrame::InitialiseHeader(IAX2Processor *iax2Processor)
880 {
881   if (iax2Processor != NULL) {
882     SetConnectionToken(iax2Processor->GetCallToken());
883     BuildTimeStamp(iax2Processor->GetCallStartTick());
884     remote   = iax2Processor->GetRemoteInfo();
885   }
886   PTRACE(5, "source timestamp is " << timeStamp);
887   frameType = (IAX2FrameType)GetFullFrameType();
888   WriteHeader();
889 }
890 
PrintOn(ostream & strm) const891 void IAX2FullFrame::PrintOn(ostream & strm) const
892 {
893   strm << IdString() << " ++  " << GetFullFrameName() << " -- "
894        << GetSubClassName() << " \"" << connectionToken << "\"" << endl
895        << remote << endl;
896 }
897 
ModifyFrameHeaderSequenceNumbers(PINDEX inNo,PINDEX outNo)898 void IAX2FullFrame::ModifyFrameHeaderSequenceNumbers(PINDEX inNo, PINDEX outNo)
899 {
900   data[8] = (BYTE) (outNo & 0xff);
901   data[9] = (BYTE) (inNo & 0xff);
902   GetSequenceInfo().SetInOutSeqNo(inNo, outNo);
903 }
904 
ModifyFrameTimeStamp(PINDEX newTimeStamp)905 void IAX2FullFrame::ModifyFrameTimeStamp(PINDEX newTimeStamp)
906 {
907   timeStamp = newTimeStamp;
908   PINDEX oldWriteIndex = currentWriteIndex;
909   currentWriteIndex = 4;
910   Write4Bytes(timeStamp);
911   currentWriteIndex = oldWriteIndex;
912 }
913 ////////////////////////////////////////////////////////////////////////////////
914 
IAX2FullFrameDtmf(const IAX2Frame & srcFrame)915 IAX2FullFrameDtmf::IAX2FullFrameDtmf(const IAX2Frame & srcFrame)
916   : IAX2FullFrame(srcFrame)
917 {
918 }
919 
IAX2FullFrameDtmf(const IAX2FullFrame & srcFrame)920 IAX2FullFrameDtmf::IAX2FullFrameDtmf(const IAX2FullFrame & srcFrame)
921   : IAX2FullFrame(srcFrame)
922 {
923 }
924 
IAX2FullFrameDtmf(IAX2Processor * iax2Processor,PString subClassValue)925 IAX2FullFrameDtmf::IAX2FullFrameDtmf(IAX2Processor *iax2Processor, PString  subClassValue)
926   : IAX2FullFrame(iax2Processor->GetEndPoint())
927 {
928   SetSubClass(subClassValue.ToUpper()[0]);
929   InitialiseHeader(iax2Processor);
930 }
931 
IAX2FullFrameDtmf(IAX2Processor * iax2Processor,char subClassValue)932 IAX2FullFrameDtmf::IAX2FullFrameDtmf(IAX2Processor *iax2Processor, char  subClassValue)
933   : IAX2FullFrame(iax2Processor->GetEndPoint())
934 {
935   SetSubClass(toupper(subClassValue));
936   InitialiseHeader(iax2Processor);
937 }
938 
GetSubClassName() const939 PString IAX2FullFrameDtmf::GetSubClassName() const {
940   switch (GetSubClass()) {
941   case dtmf0:    return PString("0");
942   case dtmf1:    return PString("1");
943   case dtmf2:    return PString("2");
944   case dtmf3:    return PString("3");
945   case dtmf4:    return PString("4");
946   case dtmf5:    return PString("5");
947   case dtmf6:    return PString("6");
948   case dtmf7:    return PString("7");
949   case dtmf8:    return PString("8");
950   case dtmf9:    return PString("9");
951   case dtmfA:    return PString("A");
952   case dtmfB:    return PString("B");
953   case dtmfC:    return PString("C");
954   case dtmfD:    return PString("D");
955   case dtmfStar: return PString("*");
956   case dtmfHash: return PString("#");
957   };
958   return PString("Undefined dtmf subclass value of ") + PString(GetSubClass());
959 }
960 
961 ////////////////////////////////////////////////////////////////////////////////
IAX2FullFrameVoice(const IAX2Frame & srcFrame)962 IAX2FullFrameVoice::IAX2FullFrameVoice(const IAX2Frame & srcFrame)
963   : IAX2FullFrame(srcFrame)
964 {
965   PTRACE(6, "Construct a full frame voice from a Frame" << IdString());
966 }
967 
IAX2FullFrameVoice(const IAX2FullFrame & srcFrame)968 IAX2FullFrameVoice::IAX2FullFrameVoice(const IAX2FullFrame & srcFrame)
969   : IAX2FullFrame(srcFrame)
970 {
971   PTRACE(6, "Construct a full frame voice from a IAX2FullFrame" << IdString());
972 }
973 
IAX2FullFrameVoice(IAX2CallProcessor * iax2Processor,PBYTEArray & sound,PINDEX usersTimeStamp)974 IAX2FullFrameVoice::IAX2FullFrameVoice(IAX2CallProcessor *iax2Processor, PBYTEArray & sound, PINDEX usersTimeStamp)
975   : IAX2FullFrame(iax2Processor->GetEndPoint())
976 {
977   if (iax2Processor != NULL) {
978     SetSubClass((PINDEX)iax2Processor->GetSelectedCodec());
979   }
980 
981   presetTimeStamp = usersTimeStamp;
982   InitialiseHeader(iax2Processor);
983 
984   PINDEX headerSize = data.GetSize();
985   data.SetSize(sound.GetSize() + headerSize);
986   memcpy(data.GetPointer() + headerSize, sound.GetPointer(), sound.GetSize());
987   PTRACE(6, "Construct a full frame voice from a processor, sound, and codec" << IdString());
988 }
989 
990 
~IAX2FullFrameVoice()991 IAX2FullFrameVoice::~IAX2FullFrameVoice()
992 {
993   PTRACE(6, "Destroy this IAX2FullFrameVoice" << IdString());
994 }
995 
GetSubClassName(unsigned int testValue)996 PString IAX2FullFrameVoice::GetSubClassName(unsigned int testValue)
997 {
998   switch (testValue) {
999   case g7231:     return PString("G.723.1");
1000   case gsm:       return PString("GSM-06.10");
1001   case g711ulaw:  return PString("G.711-uLaw-64k");
1002   case g711alaw:  return PString("G.711-ALaw-64k");
1003   case mp3:       return PString("mp3");
1004   case adpcm:     return PString("adpcm");
1005   case pcm:       return PString("pcm");
1006   case lpc10:     return PString("LPC-10");
1007   case g729:      return PString("G.729");
1008   case speex:     return PString("speex");
1009   case ilbc:      return PString("iLBC-13k3");
1010   default: ;
1011   };
1012 
1013   PStringStream res;
1014   res << "The value 0x" << ::hex << testValue << ::dec << " could not be identified as a codec";
1015   return res;
1016 }
1017 
OpalNameToIax2Value(const PString opalName)1018 unsigned short IAX2FullFrameVoice::OpalNameToIax2Value(const PString opalName)
1019 {
1020   if (opalName.Find("uLaw") != P_MAX_INDEX) {
1021     return g711ulaw;
1022   }
1023 
1024   if (opalName.Find("ALaw") != P_MAX_INDEX) {
1025     return  g711alaw;
1026   }
1027 
1028   if (opalName.Find("GSM-06.10") != P_MAX_INDEX) {
1029     return gsm;
1030   }
1031 
1032   if (opalName.Find("iLBC-13k3") != P_MAX_INDEX) {
1033     return ilbc;
1034     }
1035   PTRACE(6, "Codec " << opalName << " is not supported in IAX2");
1036   return 0;
1037 }
1038 
GetSubClassName() const1039 PString IAX2FullFrameVoice::GetSubClassName() const
1040 {
1041   return GetSubClassName(GetSubClass());
1042 }
1043 
GetOpalNameOfCodec(PINDEX testValue)1044 PString IAX2FullFrameVoice::GetOpalNameOfCodec(PINDEX testValue)
1045 {
1046   switch (testValue) {
1047   case g7231:     return PString("G.723.1");
1048   case gsm:       return PString("GSM-06.10");
1049   case g711ulaw:  return PString("G.711-uLaw-64k");
1050   case g711alaw:  return PString("G.711-ALaw-64k");
1051   case mp3:       return PString("mp3");
1052   case adpcm:     return PString("adpcm");
1053   case pcm:       return PString("Linear-16-Mono-8kHz");
1054   case lpc10:     return PString("LPC10");
1055   case g729:      return PString("G.729");
1056   case speex:     return PString("speex");
1057   case ilbc:      return PString("ilbc");
1058   default: ;
1059   };
1060 
1061   PStringStream res;
1062   res << "The value 0x" << ::hex << testValue << ::dec << " could not be identified as a codec";
1063   return res;
1064 }
1065 
1066 ////////////////////////////////////////////////////////////////////////////////
1067 
IAX2FullFrameVideo(const IAX2Frame & srcFrame)1068 IAX2FullFrameVideo::IAX2FullFrameVideo(const IAX2Frame & srcFrame)
1069   : IAX2FullFrame(srcFrame)
1070 {
1071 }
1072 
IAX2FullFrameVideo(const IAX2FullFrame & srcFrame)1073 IAX2FullFrameVideo::IAX2FullFrameVideo(const IAX2FullFrame & srcFrame)
1074   : IAX2FullFrame(srcFrame)
1075 {
1076 }
1077 
GetSubClassName() const1078 PString IAX2FullFrameVideo::GetSubClassName() const
1079 {
1080   switch (GetSubClass()) {
1081   case jpeg:  return PString("jpeg");
1082   case png:  return PString("png");
1083   case h261:  return PString("H.261");
1084   case h263:  return PString("H.263");
1085   };
1086   return PString("Undefined IAX2FullFrameVideo subclass value of ") + PString(GetSubClass());
1087 }
1088 ////////////////////////////////////////////////////////////////////////////////
1089 
IAX2FullFrameSessionControl(const IAX2Frame & srcFrame)1090 IAX2FullFrameSessionControl::IAX2FullFrameSessionControl(const IAX2Frame & srcFrame)
1091   : IAX2FullFrame(srcFrame)
1092 {
1093 }
1094 
IAX2FullFrameSessionControl(const IAX2FullFrame & srcFrame)1095 IAX2FullFrameSessionControl::IAX2FullFrameSessionControl(const IAX2FullFrame & srcFrame)
1096   : IAX2FullFrame(srcFrame)
1097 {
1098 }
1099 
IAX2FullFrameSessionControl(IAX2Processor * iax2Processor,SessionSc session)1100 IAX2FullFrameSessionControl::IAX2FullFrameSessionControl(IAX2Processor *iax2Processor,
1101 						 SessionSc session)
1102   :  IAX2FullFrame(iax2Processor->GetEndPoint())
1103 {
1104   SetSubClass((PINDEX)session);
1105   isAckFrame = PFalse;
1106   InitialiseHeader(iax2Processor);
1107   callMustBeActive = PTrue;
1108 }
1109 
IAX2FullFrameSessionControl(IAX2Processor * iax2Processor,PINDEX subClassValue)1110 IAX2FullFrameSessionControl::IAX2FullFrameSessionControl(IAX2Processor *iax2Processor,
1111 						 PINDEX subClassValue)
1112   :  IAX2FullFrame(iax2Processor->GetEndPoint())
1113 {
1114   SetSubClass(subClassValue);
1115   isAckFrame = PFalse;
1116   InitialiseHeader(iax2Processor);
1117   callMustBeActive = PTrue;
1118 }
1119 
GetSubClassName() const1120 PString IAX2FullFrameSessionControl::GetSubClassName() const {
1121   switch (GetSubClass()) {
1122   case hangup:          return PString("hangup");
1123   case ring:            return PString("ring");
1124   case ringing:         return PString("ringing");
1125   case answer:          return PString("answer");
1126   case busy:            return PString("busy");
1127   case tkoffhk:         return PString("tkoffhk");
1128   case offhook:         return PString("offhook");
1129   case congestion:      return PString("congestion");
1130   case flashhook:       return PString("flashhook");
1131   case wink:            return PString("wink");
1132   case option:          return PString("option");
1133   case keyRadio:        return PString("keyRadio");
1134   case unkeyRadio:      return PString("unkeyRadio");
1135   case callProgress:    return PString("callProgress");
1136   case callProceeding:  return PString("callProceeding");
1137   };
1138 
1139   return PString("Undefined IAX2FullFrameSessionControl subclass value of ")
1140     + PString(GetSubClass());
1141 }
1142 ////////////////////////////////////////////////////////////////////////////////
1143 
IAX2FullFrameNull(const IAX2Frame & srcFrame)1144 IAX2FullFrameNull::IAX2FullFrameNull(const IAX2Frame & srcFrame)
1145   : IAX2FullFrame(srcFrame)
1146 {
1147 }
1148 
IAX2FullFrameNull(const IAX2FullFrame & srcFrame)1149 IAX2FullFrameNull::IAX2FullFrameNull(const IAX2FullFrame & srcFrame)
1150   : IAX2FullFrame(srcFrame)
1151 {
1152 }
1153 
1154 ////////////////////////////////////////////////////////////////////////////////
1155 
IAX2FullFrameProtocol(IAX2Processor * iax2Processor,PINDEX subClassValue,ConnectionRequired needCon)1156 IAX2FullFrameProtocol::IAX2FullFrameProtocol(IAX2Processor *iax2Processor, PINDEX subClassValue, ConnectionRequired needCon)
1157   :  IAX2FullFrame(iax2Processor->GetEndPoint())
1158 {
1159   SetSubClass(subClassValue);
1160   isAckFrame = (subClassValue == cmdAck);
1161   if (isAckFrame) {
1162     PTRACE(5, "Sending an ack frame now");
1163   }
1164   InitialiseHeader(iax2Processor);
1165   callMustBeActive = (needCon == callActive);
1166   PTRACE(5, "Construct a fullframeprotocol from a processor, subclass value    and a connectionrequired. " << IdString());
1167 
1168 }
1169 
IAX2FullFrameProtocol(IAX2Processor * iax2Processor,ProtocolSc subClassValue,ConnectionRequired needCon)1170 IAX2FullFrameProtocol::IAX2FullFrameProtocol(IAX2Processor *iax2Processor,  ProtocolSc  subClassValue, ConnectionRequired needCon)
1171   : IAX2FullFrame(iax2Processor->GetEndPoint())
1172 {
1173   SetSubClass(subClassValue);
1174   isAckFrame = (subClassValue == cmdAck);
1175   InitialiseHeader(iax2Processor);
1176   callMustBeActive = (needCon == callActive);
1177 
1178   PTRACE(5, "Construct a fullframeprotocol from a processor subclass value and connection required " << IdString());
1179 }
1180 
IAX2FullFrameProtocol(IAX2Processor * iax2Processor,ProtocolSc subClassValue,IAX2FullFrame * inReplyTo,ConnectionRequired needCon)1181 IAX2FullFrameProtocol::IAX2FullFrameProtocol(IAX2Processor *iax2Processor,  ProtocolSc  subClassValue, IAX2FullFrame *inReplyTo, ConnectionRequired needCon)
1182   : IAX2FullFrame(iax2Processor->GetEndPoint())
1183 {
1184   SetSubClass(subClassValue);
1185   timeStamp = inReplyTo->GetTimeStamp();
1186   isAckFrame = (subClassValue == cmdAck);
1187   if (isAckFrame) {
1188     sequence.SetAckSequenceInfo(inReplyTo->GetSequenceInfo());
1189   }
1190   if (iax2Processor == NULL) {
1191     IAX2Remote rem = inReplyTo->GetRemoteInfo();
1192     remote = rem;
1193   } else {
1194     remote = iax2Processor->GetRemoteInfo();
1195     SetConnectionToken(iax2Processor->GetCallToken());
1196   }
1197   frameType = iax2ProtocolType;
1198   callMustBeActive = (needCon == callActive);
1199   WriteHeader();
1200   PTRACE(5, "Construct a fullframeprotocol from a  processor, subclass value and a connection required" << IdString());
1201 }
1202 
IAX2FullFrameProtocol(const IAX2Frame & srcFrame)1203 IAX2FullFrameProtocol::IAX2FullFrameProtocol(const IAX2Frame & srcFrame)
1204   : IAX2FullFrame(srcFrame)
1205 {
1206   ReadInformationElements();
1207   PTRACE(5, "Construct a fullframeprotocol from a Frame" << IdString());
1208 }
1209 
IAX2FullFrameProtocol(const IAX2FullFrame & srcFrame)1210 IAX2FullFrameProtocol::IAX2FullFrameProtocol(const IAX2FullFrame & srcFrame)
1211   : IAX2FullFrame(srcFrame)
1212 {
1213   ReadInformationElements();
1214   PTRACE(5, "Construct a fullframeprotocol from a Full Frame" << IdString());
1215 }
1216 
~IAX2FullFrameProtocol()1217 IAX2FullFrameProtocol::~IAX2FullFrameProtocol()
1218 {
1219   ieElements.AllowDeleteObjects(PTrue);
1220   PTRACE(6, "Destroy this IAX2FullFrameProtocol(" << GetSubClassName() << ") " << IdString());
1221 }
1222 
SetRetransmissionRequired()1223 void IAX2FullFrameProtocol::SetRetransmissionRequired()
1224 {
1225   if (GetSubClass() == IAX2FullFrameProtocol::cmdAck)
1226     canRetransmitFrame = PFalse;
1227 
1228   if (GetSubClass() == IAX2FullFrameProtocol::cmdLagRp)
1229     canRetransmitFrame = PFalse;
1230 
1231   if (GetSubClass() == IAX2FullFrameProtocol::cmdPong)
1232     canRetransmitFrame = PFalse;
1233 }
1234 
WriteIeAsBinaryData()1235 void IAX2FullFrameProtocol::WriteIeAsBinaryData()
1236 {
1237   PTRACE(6, "Frame\tWrite the IE data (" << ieElements.GetSize()
1238 	 << " elements) as binary data to frame " << IdString());
1239   PINDEX headerSize = data.GetSize();
1240   data.SetSize(headerSize + ieElements.GetBinaryDataSize());
1241 
1242   PINDEX i;
1243   for(i = 0; i < ieElements.GetSize(); i++) {
1244     PTRACE(6, "Frame\tAppend to outgoing frame " << *ieElements.GetIeAt(i));
1245     ieElements.GetIeAt(i)->WriteBinary(data.GetPointer(), headerSize);
1246   }
1247 }
1248 
GetCallTokenIe(IAX2IeCallToken & callToken)1249 PBoolean IAX2FullFrameProtocol::GetCallTokenIe(IAX2IeCallToken & callToken)
1250 {
1251   PINDEX i;
1252   for (i = 0; i < ieElements.GetSize(); i++) {
1253     IAX2Ie *elem = ieElements.GetIeAt(i);
1254     if (elem->GetKeyValue() == IAX2Ie::ie_callToken) {
1255       IAX2IeCallToken *src = (IAX2IeCallToken *)elem;
1256       callToken.CopyData(src);
1257       return PTrue;
1258     }
1259   }
1260   return PFalse;
1261 }
1262 
ReadInformationElements()1263 PBoolean IAX2FullFrameProtocol::ReadInformationElements()
1264 {
1265   IAX2Ie *elem = NULL;
1266 
1267   while (GetUnReadBytes() >= 2) {
1268     BYTE thisType = 0, thisLength = 0;
1269     Read1Byte(thisType);
1270     Read1Byte(thisLength);
1271     if (thisLength <= GetUnReadBytes()){
1272       elem = IAX2Ie::BuildInformationElement(thisType, thisLength, data.GetPointer() + currentReadIndex);
1273       currentReadIndex += thisLength;
1274       if (elem != NULL)
1275 	if (elem->IsValid()) {
1276 	  ieElements.Append(elem);
1277 	  //	  PTRACE(3, "Read information element " << *elem);
1278 	}
1279     } else {
1280       PTRACE(6, "Unread bytes is " << GetUnReadBytes() << " This length is " << thisLength);
1281       break;
1282     }
1283   }
1284 
1285   if (elem == NULL)
1286     return PFalse;
1287 
1288   if (!elem->IsValid())
1289     return PFalse;
1290 
1291   return GetUnReadBytes() == 0;
1292 }
1293 
GetRemoteCapability(unsigned int & capability,unsigned int & preferred)1294 void IAX2FullFrameProtocol::GetRemoteCapability(unsigned int & capability, unsigned int & preferred)
1295 {
1296   capability = 0;
1297   preferred = 0;
1298   IAX2Ie * p;
1299   PINDEX i = 0;
1300   for(;;) {
1301     p = GetIeAt(i);
1302     if (p == NULL)
1303       break;
1304 
1305     i++;
1306     if (p->IsValid()) {
1307       if (PIsDescendant(p, IAX2IeCapability)) {
1308 	capability = ((IAX2IeCapability *)p)->ReadData();
1309 	PTRACE(5, "IAX2FullFrameProtocol\tCapability codecs are " << capability);
1310       }
1311       if (PIsDescendant(p, IAX2IeFormat)) {
1312 	preferred = ((IAX2IeFormat *)p)->ReadData();
1313 	PTRACE(4, "IAX2FullFrameProtocol\tPreferred codec is " << preferred);
1314       }
1315     } else {
1316       PTRACE(3, "Invalid data in IE. ");
1317     }
1318   }
1319 }
1320 
CopyDataFromIeListTo(IAX2IeData & res)1321 void IAX2FullFrameProtocol::CopyDataFromIeListTo(IAX2IeData &res)
1322 {
1323   IAX2Ie * p;
1324   PINDEX i = 0;
1325   for(;;) {
1326     p = GetIeAt(i);
1327     if (p == NULL)
1328       break;
1329 
1330     i++;
1331     PTRACE(4, "From IAX2FullFrameProtocol, handle IAX2Ie of type " << *p);
1332     if (p->IsValid())
1333       p->StoreDataIn(res);
1334     else {
1335       PTRACE(3, "Invalid data in IE. " << *p);
1336     }
1337   }
1338 }
1339 
GetSubClassName() const1340 PString IAX2FullFrameProtocol::GetSubClassName() const
1341 {
1342   return GetSubClassName(GetSubClass());
1343 }
1344 
GetSubClassName(PINDEX t)1345 PString IAX2FullFrameProtocol::GetSubClassName(PINDEX t)
1346 {
1347   switch (t) {
1348   case cmdNew:        return PString("new");
1349   case cmdPing:       return PString("ping");
1350   case cmdPong:       return PString("pong");
1351   case cmdAck:        return PString("ack");
1352   case cmdHangup:     return PString("hangup");
1353   case cmdReject:     return PString("reject");
1354   case cmdAccept:     return PString("accept");
1355   case cmdAuthReq:    return PString("authreq");
1356   case cmdAuthRep:    return PString("authrep");
1357   case cmdInval:      return PString("inval");
1358   case cmdLagRq:      return PString("lagrq");
1359   case cmdLagRp:      return PString("lagrp");
1360   case cmdRegReq:     return PString("regreq");
1361   case cmdRegAuth:    return PString("regauth");
1362   case cmdRegAck:     return PString("regack");
1363   case cmdRegRej:     return PString("regrej");
1364   case cmdRegRel:     return PString("regrel");
1365   case cmdVnak:       return PString("vnak");
1366   case cmdDpReq:      return PString("dpreq");
1367   case cmdDpRep:      return PString("dprep");
1368   case cmdDial:       return PString("dial");
1369   case cmdTxreq:      return PString("txreq");
1370   case cmdTxcnt:      return PString("txcnt");
1371   case cmdTxacc:      return PString("txacc");
1372   case cmdTxready:    return PString("txready");
1373   case cmdTxrel:      return PString("txrel");
1374   case cmdTxrej:      return PString("txrej");
1375   case cmdQuelch:     return PString("quelch");
1376   case cmdUnquelch:   return PString("unquelch");
1377   case cmdPoke:       return PString("poke");
1378   case cmdPage:       return PString("page");
1379   case cmdMwi:        return PString("mwi");
1380   case cmdUnsupport:  return PString("unsupport");
1381   case cmdTransfer:   return PString("transfer");
1382   case cmdProvision:  return PString("provision");
1383   case cmdFwDownl:    return PString("fwDownl");
1384   case cmdFwData:     return PString("fwData");
1385   };
1386   return PString("Undefined FullFrameProtocol subclass value of ") + PString(t);
1387 }
1388 
1389 #if PTRACING
operator <<(ostream & o,IAX2FullFrameProtocol::ProtocolSc t)1390 ostream & operator << (ostream & o, IAX2FullFrameProtocol::ProtocolSc t)
1391 {
1392   PString answer = IAX2FullFrameProtocol::GetSubClassName(t);
1393   o << answer;
1394   return o;
1395 }
1396 #endif
1397 
PrintOn(ostream & strm) const1398 void IAX2FullFrameProtocol::PrintOn(ostream & strm) const
1399 {
1400   strm << "IAX2FullFrameProtocol(" << GetSubClassName() << ") "
1401        << IdString() << " -- "
1402        << " \"" << connectionToken << "\"" << endl
1403        << remote << endl;
1404 }
1405 ////////////////////////////////////////////////////////////////////////////////
1406 
IAX2FullFrameText(const IAX2Frame & srcFrame)1407 IAX2FullFrameText::IAX2FullFrameText(const IAX2Frame & srcFrame)
1408   : IAX2FullFrame(srcFrame)
1409 {
1410    if (GetMediaDataSize() > 0)
1411      internalText = PString((const char *)GetMediaDataPointer(),
1412 			    GetMediaDataSize());
1413 
1414 }
1415 
IAX2FullFrameText(const IAX2FullFrame & srcFrame)1416 IAX2FullFrameText::IAX2FullFrameText(const IAX2FullFrame & srcFrame)
1417   : IAX2FullFrame(srcFrame)
1418 {
1419   if (GetMediaDataSize() > 0)
1420     internalText = PString((const char *)GetMediaDataPointer(),
1421 			   GetMediaDataSize());
1422 
1423 }
1424 
GetSubClassName() const1425 PString IAX2FullFrameText::GetSubClassName() const
1426 {
1427   return PString("IAX2FullFrameText never has a valid sub class");
1428 }
1429 
IAX2FullFrameText(IAX2Processor * iaxProcessor,const PString & text)1430 IAX2FullFrameText::IAX2FullFrameText(IAX2Processor *iaxProcessor, const PString&  text )
1431   : IAX2FullFrame(iaxProcessor->GetEndPoint())
1432 {
1433 //  presetTimeStamp = usersTimeStamp;
1434   InitialiseHeader(iaxProcessor);
1435 
1436   internalText = text;
1437 
1438   PINDEX headerSize = data.GetSize();
1439   data.SetSize(text.GetLength() + headerSize);
1440   memcpy(data.GetPointer() + headerSize,
1441 	 internalText.GetPointer(), internalText.GetLength());
1442 
1443   PTRACE(4, "Construct a full frame text" << IdString() << " for text " << text);
1444 }
1445 
GetTextString() const1446 PString IAX2FullFrameText::GetTextString() const
1447 {
1448   return internalText;
1449 }
1450 
1451 ////////////////////////////////////////////////////////////////////////////////
1452 
IAX2FullFrameImage(const IAX2Frame & srcFrame)1453 IAX2FullFrameImage::IAX2FullFrameImage(const IAX2Frame & srcFrame)
1454   : IAX2FullFrame(srcFrame)
1455 {
1456 }
1457 
IAX2FullFrameImage(const IAX2FullFrame & srcFrame)1458 IAX2FullFrameImage::IAX2FullFrameImage(const IAX2FullFrame & srcFrame)
1459   : IAX2FullFrame(srcFrame)
1460 {
1461 }
1462 
GetSubClassName() const1463 PString IAX2FullFrameImage::GetSubClassName() const
1464 {
1465   return PString("IAX2FullFrameImage never has a valid sub class");
1466 }
1467 ////////////////////////////////////////////////////////////////////////////////
1468 
IAX2FullFrameHtml(const IAX2Frame & srcFrame)1469 IAX2FullFrameHtml::IAX2FullFrameHtml(const IAX2Frame & srcFrame)
1470   : IAX2FullFrame(srcFrame)
1471 {
1472 }
1473 
IAX2FullFrameHtml(const IAX2FullFrame & srcFrame)1474 IAX2FullFrameHtml::IAX2FullFrameHtml(const IAX2FullFrame & srcFrame)
1475   : IAX2FullFrame(srcFrame)
1476 {
1477 }
1478 
GetSubClassName() const1479 PString IAX2FullFrameHtml::GetSubClassName() const
1480 {
1481   return PString("IAX2FullFrameHtml has a sub class of ") + PString(GetSubClass());
1482 }
1483 
1484 ////////////////////////////////////////////////////////////////////////////////
1485 
IAX2FullFrameCng(const IAX2Frame & srcFrame)1486 IAX2FullFrameCng::IAX2FullFrameCng(const IAX2Frame & srcFrame)
1487   : IAX2FullFrame(srcFrame)
1488 {
1489 }
1490 
IAX2FullFrameCng(const IAX2FullFrame & srcFrame)1491 IAX2FullFrameCng::IAX2FullFrameCng(const IAX2FullFrame & srcFrame)
1492   : IAX2FullFrame(srcFrame)
1493 {
1494 }
1495 
GetSubClassName() const1496 PString IAX2FullFrameCng::GetSubClassName() const
1497 {
1498   return PString("IAX2FullFrameCng has a sub class of ") + PString(GetSubClass());
1499 }
1500 
1501 
1502 ////////////////////////////////////////////////////////////////////////////////
1503 
~IAX2FrameList()1504 IAX2FrameList::~IAX2FrameList()
1505 {
1506 }
1507 
ReportList(PString & answer)1508 void IAX2FrameList::ReportList(PString & answer)
1509 {
1510   PStringStream reply;
1511   {
1512     PWaitAndSignal m(mutex);
1513 
1514     for(PINDEX i = 0; i < PAbstractList::GetSize(); i++) {
1515        IAX2Frame *frame = (IAX2Frame *)GetAt(i);
1516        reply << "     #" << (i + 1) << " of "
1517 	     << PAbstractList::GetSize() << "   "
1518 	     << frame->GetConnectionToken() << " "
1519 	     << frame->GetTimeStamp();
1520        if (frame->IsFullFrame()) {
1521 	 IAX2FullFrame *ff = (IAX2FullFrame *)frame;
1522 	 reply << " " << ff->GetSequenceInfo().AsString() << " "
1523 	       << ff->GetFullFrameName() << endl;
1524        }
1525     }
1526   }
1527   answer = reply;
1528 }
1529 
AddNewFrame(IAX2Frame * newFrame)1530 void IAX2FrameList::AddNewFrame(IAX2Frame *newFrame)
1531 {
1532   if (newFrame == NULL)
1533     return;
1534   PTRACE(5, "Frame\tAdd " << newFrame->IdString()
1535 	 << " " << newFrame->GetRemoteInfo());
1536   PWaitAndSignal m(mutex);
1537   PAbstractList::Append(newFrame);
1538 }
1539 
GrabContents(IAX2FrameList & src)1540 void IAX2FrameList::GrabContents(IAX2FrameList &src)
1541 {
1542   IAX2Frame *current;
1543   do {
1544     current = src.GetLastFrame();
1545     AddNewFrame(current);
1546   } while (current != NULL);
1547 }
1548 
GetLastFrame()1549 IAX2Frame *IAX2FrameList::GetLastFrame()
1550 {
1551   PWaitAndSignal m(mutex);
1552   PINDEX elems = GetEntries();
1553   if (elems ==  0) {
1554     return NULL;
1555   }
1556 
1557   return (IAX2Frame *)PAbstractList::RemoveAt(0);
1558 }
1559 
DeleteMatchingSendFrame(IAX2FullFrame * reply)1560 void IAX2FrameList::DeleteMatchingSendFrame(IAX2FullFrame *reply)
1561 {
1562   PINDEX currentIndex;
1563   IAX2FullFrame *sent;
1564 
1565   PWaitAndSignal m(mutex);
1566   //Look for a frame that has been sent, which is waiting for a reply/ack.
1567   PTRACE(5, "Frame\tID# Delete matchingSendFrame start, test on "
1568 	 << reply->IdString());
1569 
1570   for (PINDEX i = 0; i < GetEntries(); i++) {
1571     sent = NULL;
1572     IAX2Frame *frame = (IAX2Frame *)GetAt(i);
1573     if (frame == NULL)
1574       continue;
1575 
1576     PTRACE(5, "ID#DeleteMatching " << frame->IdString());
1577     if (!frame->IsFullFrame())
1578       continue;
1579 
1580     sent = (IAX2FullFrame *)frame;
1581     currentIndex = i; /* If a match is found, we delete this one */
1582 
1583     if (sent->DeleteFrameNow()) {
1584       // Skip this frame, as it is marked, delete now
1585       continue;
1586     }
1587 
1588     if (sent->IsNewFrame() &&
1589 	reply->IsCallTokenFrame()) {
1590       if(sent->GetRemoteInfo().SourceCallNumber() ==
1591 	 reply->GetRemoteInfo().DestCallNumber())
1592 	goto foundMatch;
1593     }
1594 
1595     if (!(sent->GetRemoteInfo() *= reply->GetRemoteInfo())) {
1596       PTRACE(5, "mismatch in remote info");
1597       continue;
1598     }
1599 
1600     if (sent->IsNewFrame() &&
1601 	reply->GetSequenceInfo().IsFirstReplyFrame()) {
1602       PTRACE(5, "Frame\tHave a match on a new frame we sent out");
1603       goto foundMatch;
1604     }
1605 
1606     if (sent->IsRegReqFrame() &&
1607         (reply->IsRegAckFrame() || reply->IsRegAuthFrame() || reply->IsRegRejFrame())) {
1608       PTRACE(5, "have read a RegAck, RegAuth or RegRej packet for a RegReq frame we have sent, delete this RegReq");
1609       PTRACE(5, "reg type frame, so MarkDeleteNow on " << sent->IdString());
1610       goto foundMatch;
1611     }
1612 
1613     if (sent->IsRegRelFrame() &&
1614         (reply->IsRegAckFrame() || reply->IsRegAuthFrame() || reply->IsRegRejFrame())) {
1615       PTRACE(5, "have read a RegAck, RegAuth or RegRej packet for a RegRel frame we have sent, delete this RegRel");
1616       PTRACE(5, "reg rel/authoframe, so MarkDeleteNow on " << sent->IdString());
1617       goto foundMatch;
1618     }
1619 
1620     if (sent->GetTimeStamp() != reply->GetTimeStamp()) {
1621       PTRACE(5, "Time stamps differ, so give up on the test" << sent->IdString());
1622       continue;
1623     } else {
1624       PTRACE(5, "Time stamps are the same, so check in seqno vs oseqno " << sent->IdString());
1625     }
1626 
1627     PTRACE(5, "SeqNos\tSent is " << sent->GetSequenceInfo().OutSeqNo()
1628 	   << " " << sent->GetSequenceInfo().InSeqNo());
1629     PTRACE(5, "SeqNos\tRepl is " << reply->GetSequenceInfo().OutSeqNo()
1630 	   << " " << reply->GetSequenceInfo().InSeqNo());
1631 
1632     if (reply->IsLagRpFrame() && sent->IsLagRqFrame()) {
1633       PTRACE(5, "have read a LagRp packet for a LagRq frame  we have sent, delete this LagRq "
1634 	     << sent->IdString());
1635       PTRACE(5, "LAG frame, so MarkDeleteNow on " << sent->IdString());
1636       goto foundMatch;
1637     }
1638 
1639     if (reply->IsPongFrame() && sent->IsPingFrame()) {
1640       PTRACE(5, "have read a Pong packet for a PING frame  we have sent: delete the Pong "
1641 	     << sent->IdString());
1642       PTRACE(5, "PONG frame, so MarkDeleteNow on " << sent->IdString());
1643       goto foundMatch;
1644     }
1645 
1646     if (sent->GetSequenceInfo().InSeqNo() == reply->GetSequenceInfo().OutSeqNo()) {
1647       PTRACE(5, "Timestamp, and inseqno matches oseqno " << sent->IdString());
1648       if (reply->IsAckFrame()) {
1649         PTRACE(5, "have read an ack packet for one we have sent, so delete this one " << sent->IdString());
1650 	PTRACE(5, "ack for existing frame, MarkDeleteNow " << sent->IdString());
1651         goto foundMatch;
1652       }
1653     } else {
1654       PTRACE(5, "No match:: sent=" << sent->IdString() << " and reply=" << reply->IdString()
1655 	     << PString(reply->IsAckFrame() ? "reply is ack frame " : "reply is not ack frame ")
1656 	     << PString("Sequence numbers are:: sentIn" )
1657 	     << sent->GetSequenceInfo().InSeqNo() << "  rcvdOut" << reply->GetSequenceInfo().OutSeqNo());
1658     }
1659 
1660     PTRACE(5, " sequence " << sent->GetSequenceInfo().OutSeqNo()
1661 	   << " and " << reply->GetSequenceInfo().InSeqNo() << " are different");
1662 
1663   }
1664   // No match found, so no sent frame will be deleted
1665   return;
1666 
1667  foundMatch:
1668 
1669   delete sent;
1670   RemoveAt(currentIndex);
1671   return;
1672 }
1673 
SendVnakRequestedFrames(IAX2FullFrameProtocol & src)1674 void IAX2FrameList::SendVnakRequestedFrames(IAX2FullFrameProtocol &src)
1675 {
1676   PINDEX srcOutSeqNo = src.GetSequenceInfo().OutSeqNo();
1677   PWaitAndSignal m(mutex);
1678   PTRACE(4, "Look for a frame that has been sent, waiting to be acked etc, that matches the supplied Vnak frame");
1679 
1680   for (PINDEX i = 0; i < GetEntries(); i++) {
1681     IAX2Frame *frame = (IAX2Frame *)GetAt(i);
1682     if (frame == NULL)
1683       continue;
1684 
1685     if (!frame->IsFullFrame())
1686       continue;
1687 
1688     IAX2FullFrame *sent = (IAX2FullFrame *)frame;
1689 
1690     if (sent->DeleteFrameNow()) {
1691       PTRACE(4, "Skip this frame, as it is marked, delete now" << sent->IdString());
1692       continue;
1693     }
1694 
1695     if (!(sent->GetRemoteInfo() *= src.GetRemoteInfo())) {
1696       PTRACE(5, "mismatch in remote info");
1697       continue;
1698     }
1699 
1700     if (sent->GetSequenceInfo().OutSeqNo() <= srcOutSeqNo) {
1701       sent->MarkVnakSendNow();
1702     }
1703   }
1704 }
1705 
Initialise()1706 void IAX2FrameList::Initialise()
1707 {
1708   PWaitAndSignal m(mutex);
1709   DisallowDeleteObjects();
1710 }
1711 
GetResendFramesDeleteOldFrames(IAX2FrameList & framesToSend)1712 void IAX2FrameList::GetResendFramesDeleteOldFrames(IAX2FrameList &framesToSend)
1713 {
1714   PWaitAndSignal m(mutex);
1715   PTRACE(5, "ID# GetResendFramesDeleteOldFrames start");
1716 
1717   if (GetSize() == 0) {
1718     PTRACE(5, "No frames to be resent.");
1719     PTRACE(5, "ID# GetResendFramesDeleteOldFrames end cause empty");
1720     return;
1721   }
1722 
1723   for (PINDEX i = GetEntries(); i > 0; i--) {
1724     IAX2FullFrame *active = (IAX2FullFrame *)PAbstractList::GetAt(i - 1);
1725     if (active == NULL)
1726       continue;
1727 
1728     if (active->DeleteFrameNow()) {
1729       PTRACE(5, "marked as delete now, so delete" << *active);
1730       delete active;
1731       active = NULL;
1732       PAbstractList::RemoveAt(i - 1);
1733       continue;
1734       }
1735 
1736     if (active->SendFrameNow()) {
1737       PAbstractList::RemoveAt(i - 1);
1738       framesToSend.AddNewFrame(active);
1739     }
1740   }
1741   PTRACE(4, "Have collected " << framesToSend.GetSize() << " frames to onsend");
1742   PTRACE(5, "ID# GetResendFramesDeleteOldFrames end ");
1743   return;
1744 }
1745 
MarkAllAsResent()1746 void IAX2FrameList::MarkAllAsResent()
1747 {
1748   PWaitAndSignal m(mutex);
1749 
1750   for (PINDEX i = 0; i < GetEntries(); i++) {
1751     IAX2FullFrame *active = (IAX2FullFrame *)PAbstractList::GetAt(i);
1752     active->MarkAsResent();
1753   }
1754 }
1755 
1756 
1757 #endif // OPAL_IAX2
1758 
1759 
1760 ////////////////////////////////////////////////////////////////////////////////
1761 /* The comment below is magic for those who use emacs to edit this file.
1762  * With the comment below, the tab key does auto indent to 2 spaces.
1763  *
1764  * Local Variables:
1765  * mode:c
1766  * c-basic-offset:2
1767  * End:
1768  */
1769 
1770