Coverage Report

Created: 2026-01-09 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/resiprocate/resip/stack/MsgHeaderScanner.cxx
Line
Count
Source
1
#if defined(HAVE_CONFIG_H)
2
#include "config.h"
3
#endif
4
5
#include <ctype.h>
6
#include <limits.h>
7
#include <stdio.h>
8
#include "resip/stack/HeaderTypes.hxx"
9
#include "resip/stack/SipMessage.hxx"
10
#include "resip/stack/MsgHeaderScanner.hxx"
11
#include "rutil/WinLeakCheck.hxx"
12
#include "rutil/Logger.hxx"
13
14
2.78k
#define RESIPROCATE_SUBSYSTEM Subsystem::SIP
15
16
namespace resip 
17
{
18
19
///////////////////////////////////////////////////////////////////////////////
20
//   Any character could be used as the chunk terminating sentinel, as long as
21
//   it would otherwise be character category "other".  The null character
22
//   was chosen because it is unlikely to occur naturally -- but it's OK if it
23
//   does.
24
25
enum { chunkTermSentinelChar = '\0' };
26
27
enum CharCategoryEnum
28
{
29
   ccChunkTermSentinel,
30
   ccOther,
31
   ccFieldName,
32
   ccWhitespace,
33
   ccColon,
34
   ccDoubleQuotationMark,
35
   ccLeftAngleBracket,
36
   ccRightAngleBracket,
37
   ccBackslash,
38
   ccComma,
39
   ccCarriageReturn,
40
   ccLineFeed,
41
   numCharCategories
42
};
43
typedef char CharCategory;
44
45
char* 
46
MsgHeaderScanner::allocateBuffer(size_t size)
47
0
{
48
0
   return new char[size + MaxNumCharsChunkOverflow];
49
0
}
50
51
struct CharInfo
52
{
53
      CharCategory category;
54
      MsgHeaderScanner::TextPropBitMask textPropBitMask;
55
};
56
    
57
static CharInfo charInfoArray[UCHAR_MAX+1];
58
    
59
static inline int c2i(unsigned char c)
60
1.98k
{
61
1.98k
   return static_cast<int>(c); 
62
1.98k
}
63
64
static void initCharInfoArray()
65
1
{
66
257
   for(unsigned int charIndex = 0; charIndex <= UCHAR_MAX; ++charIndex) 
67
256
   {
68
256
      charInfoArray[charIndex].category = ccOther;
69
256
      charInfoArray[charIndex].textPropBitMask = 0;
70
256
   }
71
72
1
   for(const char *charPtr = "abcdefghijklmnopqrstuvwxyz"
73
1
          "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.!%*_+`'~";
74
73
       *charPtr;
75
72
       ++charPtr)
76
72
   {
77
72
      charInfoArray[c2i(*charPtr)].category = ccFieldName;
78
72
   }
79
80
1
   charInfoArray[c2i(' ')].category  = ccWhitespace;
81
1
   charInfoArray[c2i('\t')].category = ccWhitespace;
82
1
   charInfoArray[c2i(':')].category  = ccColon;
83
1
   charInfoArray[c2i('"')].category  = ccDoubleQuotationMark;
84
1
   charInfoArray[c2i('<')].category  = ccLeftAngleBracket;
85
1
   charInfoArray[c2i('>')].category  = ccRightAngleBracket;
86
1
   charInfoArray[c2i('\\')].category  = ccBackslash;
87
1
   charInfoArray[c2i(',')].category  = ccComma;
88
1
   charInfoArray[c2i('\r')].category = ccCarriageReturn;
89
1
   charInfoArray[c2i('\n')].category = ccLineFeed;
90
   // Assert: "chunkTermSentinelChar"'s category is still the default "ccOther".
91
1
   charInfoArray[c2i(chunkTermSentinelChar)].category = ccChunkTermSentinel;
92
   // Init text property bit masks.
93
1
   charInfoArray[c2i('\r')].textPropBitMask =
94
1
      MsgHeaderScanner::tpbmContainsLineBreak;
95
1
   charInfoArray[c2i('\n')].textPropBitMask =
96
1
      MsgHeaderScanner::tpbmContainsLineBreak;
97
1
   charInfoArray[c2i(' ')].textPropBitMask =
98
1
      MsgHeaderScanner::tpbmContainsWhitespace;
99
1
   charInfoArray[c2i('\t')].textPropBitMask =
100
1
      MsgHeaderScanner::tpbmContainsWhitespace;
101
1
   charInfoArray[c2i('\\')].textPropBitMask =
102
1
      MsgHeaderScanner::tpbmContainsBackslash;
103
1
   charInfoArray[c2i('%')].textPropBitMask =
104
1
      MsgHeaderScanner::tpbmContainsPercent;
105
1
   charInfoArray[c2i(';')].textPropBitMask =
106
1
      MsgHeaderScanner::tpbmContainsSemicolon;
107
1
   charInfoArray[c2i('(')].textPropBitMask =
108
1
      MsgHeaderScanner::tpbmContainsParen;
109
1
   charInfoArray[c2i(')')].textPropBitMask =
110
1
      MsgHeaderScanner::tpbmContainsParen;
111
1
}
112
113
///////////////////////////////////////////////////////////////////////////////
114
//   States marked '1' scan normal values.  States marked 'N' scan multi-values.
115
116
enum StateEnum
117
{
118
   sMsgStart,
119
   sHalfLineBreakAtMsgStart,
120
   sScanStatusLine,
121
   sHalfLineBreakAfterStatusLine,
122
   sAfterLineBreakAfterStatusLine,
123
   sScanFieldName,
124
   sScanWhitespaceAfter1FieldName,
125
   sScanWhitespaceAfterNFieldName,
126
   sScanWhitespaceOr1Value,
127
   sScanWhitespaceOrNValue,
128
   sHalfLineBreakInWhitespaceBefore1Value,
129
   sHalfLineBreakInWhitespaceBeforeNValue,
130
   sAfterLineBreakInWhitespaceBefore1Value,
131
   sAfterLineBreakInWhitespaceBeforeNValue,
132
   sScan1Value,
133
   sScanNValue,
134
   sHalfLineBreakIn1Value,
135
   sHalfLineBreakInNValue,
136
   sAfterLineBreakIn1Value,
137
   sAfterLineBreakInNValue,
138
   sScanNValueInQuotes,
139
   sAfterEscCharInQuotesInNValue,
140
   sHalfLineBreakInQuotesInNValue,
141
   sAfterLineBreakInQuotesInNValue,
142
   sScanNValueInAngles,
143
   sHalfLineBreakInAnglesInNValue,
144
   sAfterLineBreakInAnglesInNValue,
145
   sHalfLineBreakAfterLineBreak,
146
   numStates
147
};
148
149
typedef char State;
150
151
// For each '1' state, the 'N' state is "deltaOfNStateFrom1State" larger.
152
enum { deltaOfNStateFrom1State = 1 };
153
154
/////
155
    
156
enum TransitionActionEnum {
157
   taNone,
158
   taTermStatusLine,       // The current character terminates the status
159
   //     line.
160
   taTermFieldName,        // The current character terminates a field name.
161
   //     If the field supports multi-values, shift
162
   //     the state machine into multi-value scanning.
163
   taBeyondEmptyValue,     // The current character terminates an empty value.
164
   //     Implies taStartText.
165
   taTermValueAfterLineBreak, 
166
   // The previous two characters are a linebreak
167
   //      terminating a value.  Implies taStartText.
168
   taTermValue,            // The current character terminates a value.
169
   taStartText,            // The current character starts a text unit.
170
   //     (The status line, a field name, or a value.)
171
   taEndHeader,            // The current character mEnds_ the header.
172
   taChunkTermSentinel,    // Either the current character terminates the
173
   //    current chunk or it is an ordinary character.
174
   taError                 // The input is erroneous.
175
};
176
typedef char TransitionAction;
177
178
179
struct TransitionInfo
180
{
181
      TransitionAction  action;
182
      State             nextState;
183
};
184
185
static TransitionInfo stateMachine[numStates][numCharCategories];
186
187
inline void specTransition(State state,
188
                           CharCategory charCategory,
189
                           TransitionAction action,
190
                           State nextState)
191
474
{
192
474
   stateMachine[c2i(state)][c2i(charCategory)].action = action;
193
474
   stateMachine[c2i(state)][c2i(charCategory)].nextState = nextState;
194
474
}
195
196
static void specDefaultTransition(State state,
197
                                  TransitionAction action,
198
                                  State nextState)
199
28
{
200
28
   for (int charCategory = 0;
201
364
        charCategory < numCharCategories;
202
336
        ++charCategory) 
203
336
   {
204
336
      specTransition(state, charCategory, action, nextState);
205
336
   }
206
28
   specTransition(state, ccCarriageReturn, taError, state);
207
28
   specTransition(state, ccLineFeed, taError, state);
208
28
   specTransition(state, ccChunkTermSentinel, taChunkTermSentinel, state);
209
28
}
210
211
static void specHalfLineBreakState(State halfLineBreakState,
212
                                   State  afterLineBreakState)
213
9
{
214
9
   specDefaultTransition(halfLineBreakState, taError, halfLineBreakState);
215
9
   specTransition(halfLineBreakState, ccLineFeed, taNone, afterLineBreakState);
216
9
}
217
218
219
//   Single-value (1) scanning and multi-value (N) scanning involves several nearly
220
//   identical states.
221
//   "stateDelta" is either 0 or "deltaOfNStateFrom1State".
222
223
static void specXValueStates(int  stateDelta)
224
2
{
225
2
   specDefaultTransition(sScanWhitespaceAfter1FieldName + stateDelta,
226
2
                         taError,
227
2
                         sScanWhitespaceAfter1FieldName + stateDelta);
228
2
   specTransition(sScanWhitespaceAfter1FieldName + stateDelta,
229
2
                  ccWhitespace,
230
2
                  taNone,
231
2
                  sScanWhitespaceAfter1FieldName + stateDelta);
232
2
   specTransition(sScanWhitespaceAfter1FieldName + stateDelta,
233
2
                  ccColon,
234
2
                  taNone,
235
2
                  sScanWhitespaceOr1Value + stateDelta);
236
2
   specDefaultTransition(sScanWhitespaceOr1Value + stateDelta,
237
2
                         taStartText,
238
2
                         sScan1Value + stateDelta);
239
2
   specTransition(sScanWhitespaceOr1Value + stateDelta,
240
2
                  ccWhitespace,
241
2
                  taNone,
242
2
                  sScanWhitespaceOr1Value + stateDelta);
243
2
   if (stateDelta == deltaOfNStateFrom1State)
244
1
   {
245
1
      specTransition(sScanWhitespaceOr1Value + stateDelta,
246
1
                     ccComma,
247
1
                     taError,
248
1
                     sScanWhitespaceOr1Value + stateDelta);
249
1
      specTransition(sScanWhitespaceOr1Value + stateDelta,
250
1
                     ccLeftAngleBracket,
251
1
                     taStartText,
252
1
                     sScanNValueInAngles);
253
1
      specTransition(sScanWhitespaceOr1Value + stateDelta,
254
1
                     ccDoubleQuotationMark,
255
1
                     taStartText,
256
1
                     sScanNValueInQuotes);
257
1
   }
258
2
   specTransition(sScanWhitespaceOr1Value + stateDelta,
259
2
                  ccCarriageReturn,
260
2
                  taNone,
261
2
                  sHalfLineBreakInWhitespaceBefore1Value + stateDelta);
262
2
   specHalfLineBreakState(sHalfLineBreakInWhitespaceBefore1Value + stateDelta,
263
2
                          sAfterLineBreakInWhitespaceBefore1Value + stateDelta);
264
2
   specDefaultTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta,
265
2
                         taError,
266
2
                         sAfterLineBreakInWhitespaceBefore1Value + stateDelta);
267
2
   specTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta,
268
2
                  ccFieldName,
269
2
                  taBeyondEmptyValue,
270
2
                  sScanFieldName);
271
2
   specTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta,
272
2
                  ccWhitespace,
273
2
                  taNone,
274
2
                  sScanWhitespaceOr1Value + stateDelta);
275
2
   specTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta,
276
2
                  ccCarriageReturn,
277
2
                  taBeyondEmptyValue,
278
2
                  sHalfLineBreakAfterLineBreak);
279
2
   specDefaultTransition(sScan1Value + stateDelta,
280
2
                         taNone,
281
2
                         sScan1Value + stateDelta);
282
2
   if (stateDelta == deltaOfNStateFrom1State)
283
1
   {
284
1
      specTransition(sScan1Value + stateDelta,
285
1
                     ccComma,
286
1
                     taTermValue,
287
1
                     sScanWhitespaceOr1Value + stateDelta);
288
1
      specTransition(sScan1Value + stateDelta,
289
1
                     ccLeftAngleBracket,
290
1
                     taNone,
291
1
                     sScanNValueInAngles);
292
1
      specTransition(sScan1Value + stateDelta,
293
1
                     ccDoubleQuotationMark,
294
1
                     taNone,
295
1
                     sScanNValueInQuotes);
296
1
   }
297
2
   specTransition(sScan1Value + stateDelta,
298
2
                  ccCarriageReturn,
299
2
                  taNone,
300
2
                  sHalfLineBreakIn1Value + stateDelta);
301
2
   specHalfLineBreakState(sHalfLineBreakIn1Value + stateDelta,
302
2
                          sAfterLineBreakIn1Value + stateDelta);
303
2
   specDefaultTransition(sAfterLineBreakIn1Value + stateDelta,
304
2
                         taError,
305
2
                         sAfterLineBreakIn1Value + stateDelta);
306
2
   specTransition(sAfterLineBreakIn1Value + stateDelta,
307
2
                  ccFieldName,
308
2
                  taTermValueAfterLineBreak,
309
2
                  sScanFieldName);
310
2
   specTransition(sAfterLineBreakIn1Value + stateDelta,
311
2
                  ccWhitespace,
312
2
                  taNone,
313
2
                  sScan1Value + stateDelta);
314
2
   specTransition(sAfterLineBreakIn1Value + stateDelta,
315
2
                  ccCarriageReturn,
316
2
                  taTermValueAfterLineBreak,
317
2
                  sHalfLineBreakAfterLineBreak);
318
2
}
319
320
static void initStateMachine()
321
1
{
322
   // By convention, error transitions maintain the same state.
323
1
   specDefaultTransition(sMsgStart, taStartText, sScanStatusLine);
324
1
   specTransition(sMsgStart,
325
1
                  ccCarriageReturn,
326
1
                  taNone,
327
1
                  sHalfLineBreakAtMsgStart);
328
1
   specTransition(sMsgStart, ccLineFeed, taError, sMsgStart);
329
1
   specHalfLineBreakState(sHalfLineBreakAtMsgStart, sMsgStart);
330
1
   specDefaultTransition(sScanStatusLine, taNone, sScanStatusLine);
331
1
   specTransition(sScanStatusLine,
332
1
                  ccCarriageReturn,
333
1
                  taTermStatusLine,
334
1
                  sHalfLineBreakAfterStatusLine);
335
1
   specHalfLineBreakState(sHalfLineBreakAfterStatusLine,
336
1
                          sAfterLineBreakAfterStatusLine);
337
1
   specDefaultTransition(sAfterLineBreakAfterStatusLine,
338
1
                         taError,
339
1
                         sAfterLineBreakAfterStatusLine);
340
1
   specTransition(sAfterLineBreakAfterStatusLine,
341
1
                  ccFieldName,
342
1
                  taStartText,
343
1
                  sScanFieldName);
344
1
   specTransition(sAfterLineBreakAfterStatusLine,
345
1
                  ccWhitespace,
346
1
                  taError,
347
1
                  sAfterLineBreakAfterStatusLine);
348
1
   specTransition(sAfterLineBreakAfterStatusLine,
349
1
                  ccCarriageReturn,
350
1
                  taNone,
351
1
                  sHalfLineBreakAfterLineBreak);
352
1
   specDefaultTransition(sScanFieldName, taError, sScanFieldName);
353
1
   specTransition(sScanFieldName, ccFieldName, taNone, sScanFieldName);
354
1
   specTransition(sScanFieldName,
355
1
                  ccWhitespace,
356
1
                  taTermFieldName,
357
1
                  sScanWhitespaceAfter1FieldName);
358
1
   specTransition(sScanFieldName,
359
1
                  ccColon,
360
1
                  taTermFieldName,
361
1
                  sScanWhitespaceOr1Value);
362
1
   specXValueStates(0);
363
1
   specXValueStates(deltaOfNStateFrom1State);
364
1
   specDefaultTransition(sScanNValueInQuotes, taNone, sScanNValueInQuotes);
365
1
   specTransition(sScanNValueInQuotes,
366
1
                  ccDoubleQuotationMark,
367
1
                  taNone,
368
1
                  sScanNValue);
369
1
   specTransition(sScanNValueInQuotes,
370
1
                  ccBackslash,
371
1
                  taNone,
372
1
                  sAfterEscCharInQuotesInNValue);
373
1
   specTransition(sScanNValueInQuotes,
374
1
                  ccCarriageReturn,
375
1
                  taNone,
376
1
                  sHalfLineBreakInQuotesInNValue);
377
1
   specDefaultTransition(sAfterEscCharInQuotesInNValue,
378
1
                         taNone,
379
1
                         sScanNValueInQuotes);
380
1
   specHalfLineBreakState(sHalfLineBreakInQuotesInNValue,
381
1
                          sAfterLineBreakInQuotesInNValue);
382
1
   specDefaultTransition(sAfterLineBreakInQuotesInNValue,
383
1
                         taError,
384
1
                         sAfterLineBreakInQuotesInNValue);
385
1
   specTransition(sAfterLineBreakInQuotesInNValue,
386
1
                  ccWhitespace,
387
1
                  taNone,
388
1
                  sScanNValueInQuotes);
389
1
   specDefaultTransition(sScanNValueInAngles, taNone, sScanNValueInAngles);
390
1
   specTransition(sScanNValueInAngles,
391
1
                  ccRightAngleBracket,
392
1
                  taNone,
393
1
                  sScanNValue);
394
1
   specTransition(sScanNValueInAngles,
395
1
                  ccCarriageReturn,
396
1
                  taNone,
397
1
                  sHalfLineBreakInAnglesInNValue);
398
1
   specHalfLineBreakState(sHalfLineBreakInAnglesInNValue,
399
1
                          sAfterLineBreakInAnglesInNValue);
400
1
   specDefaultTransition(sAfterLineBreakInAnglesInNValue,
401
1
                         taError,
402
1
                         sAfterLineBreakInAnglesInNValue);
403
1
   specTransition(sAfterLineBreakInAnglesInNValue,
404
1
                  ccWhitespace,
405
1
                  taNone,
406
1
                  sScanNValueInAngles);
407
1
   specHalfLineBreakState(sHalfLineBreakAfterLineBreak, sMsgStart);
408
409
   // Most half-line-break states do nothing when they read a line feed,
410
   // but sHalfLineBreakAfterLineBreak must end the message header scanning.
411
412
1
   specTransition(sHalfLineBreakAfterLineBreak,
413
1
                  ccLineFeed,
414
1
                  taEndHeader,
415
1
                  sMsgStart); // Arbitrary but possibly handy.
416
1
}
417
418
// Debug follows
419
#if defined(RESIP_MSG_HEADER_SCANNER_DEBUG)  
420
421
static void printText(const char *  text,
422
                      unsigned int  textLength)
423
{
424
   const char *charPtr = text;
425
   for (unsigned int counter = 0; counter < textLength; ++charPtr, ++counter)
426
   {
427
      char c = *charPtr;
428
      switch (c)
429
      {
430
         case '\\': printf("\\\\");
431
            break;
432
         case '\r': printf("\\r");
433
            break;
434
         case '\n': printf("\\n");
435
            break;
436
         case '\t': printf("\\t");
437
            break;
438
         case '\0': printf("\\0");
439
            break;
440
         default:   putchar(c);
441
      }
442
   }
443
}
444
445
static const char *
446
categorySymbol(CharCategory c)
447
{
448
   switch(c)
449
   {
450
      case ccChunkTermSentinel: return "TERM";
451
      case ccOther: return "*";
452
      case ccFieldName: return "FName";
453
      case ccWhitespace: return "WS";
454
      case ccColon: return "\\\":\\\"";
455
      case ccDoubleQuotationMark: return "\\\"";
456
      case ccLeftAngleBracket: return "\\\"<\\\"";
457
      case ccRightAngleBracket: return "\\\">\\\"";
458
      case ccBackslash: return "\\\"\\\\\\\"";
459
      case ccComma: return "\\\",\\\"";
460
      case ccCarriageReturn: return "CR";
461
      case ccLineFeed: return "LF";
462
   }
463
   return "??CC??";
464
}
465
466
static const char *
467
categoryName(CharCategory c)
468
{
469
   switch(c)
470
   {
471
      case ccChunkTermSentinel: return "ccChunkTermSentinel";
472
      case ccOther: return "ccOther";
473
      case ccFieldName: return "ccFieldName";
474
      case ccWhitespace: return "ccWhitespace";
475
      case ccColon: return "ccColon";
476
      case ccDoubleQuotationMark: return "ccDoubleQuotationMark";
477
      case ccLeftAngleBracket: return "ccLeftAngleBracket";
478
      case ccRightAngleBracket: return "ccRightAngleBracket";
479
      case ccBackslash: return "ccBackslash";
480
      case ccComma: return "ccComma";
481
      case ccCarriageReturn: return "ccCarriageReturn";
482
      case ccLineFeed: return "ccLineFeed";
483
   }
484
   return "UNKNOWNCC";
485
}
486
487
static const char *
488
cleanName(const char * name)
489
{
490
   // Remove leading type-noise from name
491
   static char *leaders[] = {
492
      "cc",
493
      "s",
494
      "taChunkTerm", // hack to make ChunkTermSentinel smaller
495
      "ta"
496
   };
497
   const int nLeaders = sizeof(leaders)/sizeof(*leaders);
498
   int offset = 0;
499
   for(int i = 0 ; i < nLeaders ; i++)
500
   {
501
      unsigned int l = strlen(leaders[i]);
502
      if (strstr(name,leaders[i]) == name &&
503
          strlen(name) > l && 
504
          isupper(static_cast< unsigned char >(name[l])))
505
      {
506
         offset = l;
507
         break;
508
      }
509
   }
510
   return &name[offset];
511
}
512
513
static const char * 
514
stateName(State state)
515
{
516
   const char *stateName;
517
   switch (state) 
518
   {
519
      case sMsgStart:
520
         stateName = "sMsgStart";
521
         break;
522
      case sHalfLineBreakAtMsgStart:
523
         stateName = "sHalfLineBreakAtMsgStart";
524
         break;
525
      case sScanStatusLine:
526
         stateName = "sScanStatusLine";
527
         break;
528
      case sHalfLineBreakAfterStatusLine:
529
         stateName = "sHalfLineBreakAfterStatusLine";
530
         break;
531
      case sAfterLineBreakAfterStatusLine:
532
         stateName = "sAfterLineBreakAfterStatusLine";
533
         break;
534
      case sScanFieldName:
535
         stateName = "sScanFieldName";
536
         break;
537
      case sScanWhitespaceAfter1FieldName:
538
         stateName = "sScanWhitespaceAfter1FieldName";
539
         break;
540
      case sScanWhitespaceAfterNFieldName:
541
         stateName = "sScanWhitespaceAfterNFieldName";
542
         break;
543
      case sScanWhitespaceOr1Value:
544
         stateName = "sScanWhitespaceOr1Value";
545
         break;
546
      case sScanWhitespaceOrNValue:
547
         stateName = "sScanWhitespaceOrNValue";
548
         break;
549
      case sHalfLineBreakInWhitespaceBefore1Value:
550
         stateName = "sHalfLineBreakInWhitespaceBefore1Value";
551
         break;
552
      case sHalfLineBreakInWhitespaceBeforeNValue:
553
         stateName = "sHalfLineBreakInWhitespaceBeforeNValue";
554
         break;
555
      case sAfterLineBreakInWhitespaceBefore1Value:
556
         stateName = "sAfterLineBreakInWhitespaceBefore1Value";
557
         break;
558
      case sAfterLineBreakInWhitespaceBeforeNValue:
559
         stateName = "sAfterLineBreakInWhitespaceBeforeNValue";
560
         break;
561
      case sScan1Value:
562
         stateName = "sScan1Value";
563
         break;
564
      case sScanNValue:
565
         stateName = "sScanNValue";
566
         break;
567
      case sHalfLineBreakIn1Value:
568
         stateName = "sHalfLineBreakIn1Value";
569
         break;
570
      case sHalfLineBreakInNValue:
571
         stateName = "sHalfLineBreakInNValue";
572
         break;
573
      case sAfterLineBreakIn1Value:
574
         stateName = "sAfterLineBreakIn1Value";
575
         break;
576
      case sAfterLineBreakInNValue:
577
         stateName = "sAfterLineBreakInNValue";
578
         break;
579
      case sScanNValueInQuotes:
580
         stateName = "sScanNValueInQuotes";
581
         break;
582
      case sAfterEscCharInQuotesInNValue:
583
         stateName = "sAfterEscCharInQuotesInNValue";
584
         break;
585
      case sHalfLineBreakInQuotesInNValue:
586
         stateName = "sHalfLineBreakInQuotesInNValue";
587
         break;
588
      case sAfterLineBreakInQuotesInNValue:
589
         stateName = "sAfterLineBreakInQuotesInNValue";
590
         break;
591
      case sScanNValueInAngles:
592
         stateName = "sScanNValueInAngles";
593
         break;
594
      case sHalfLineBreakInAnglesInNValue:
595
         stateName = "sHalfLineBreakInAnglesInNValue";
596
         break;
597
      case sAfterLineBreakInAnglesInNValue:
598
         stateName = "sAfterLineBreakInAnglesInNValue";
599
         break;
600
      case sHalfLineBreakAfterLineBreak:
601
         stateName = "sHalfLineBreakAfterLineBreak";
602
         break;
603
      default:
604
         stateName = "<unknown>";
605
   }//switch
606
   return stateName;
607
}
608
609
static const char *
610
trActionName(TransitionAction transitionAction)
611
{  
612
   const char *transitionActionName;
613
   switch (transitionAction)
614
   {
615
      case taNone:
616
         transitionActionName = "taNone";
617
         break;
618
      case taTermStatusLine:
619
         transitionActionName = "taTermStatusLine";
620
         break;
621
      case taTermFieldName:
622
         transitionActionName = "taTermFieldName";
623
         break;
624
      case taBeyondEmptyValue:
625
         transitionActionName = "taBeyondEmptyValue";
626
         break;
627
      case taTermValueAfterLineBreak:
628
         transitionActionName = "taTermValueAfterLineBreak";
629
         break;
630
      case taTermValue:
631
         transitionActionName = "taTermValue";
632
         break;
633
      case taStartText:
634
         transitionActionName = "taStartText";
635
         break;
636
      case taEndHeader:
637
         transitionActionName = "taEndHeader";
638
         break;
639
      case taChunkTermSentinel:
640
         transitionActionName = "taChunkTermSentinel";
641
         break;
642
      case taError:
643
         transitionActionName = "taError";
644
         break;
645
      default:
646
         transitionActionName = "<unknown>";
647
   }
648
   return transitionActionName;
649
}
650
651
static void
652
printStateTransition(State state,
653
                     char character,
654
                     TransitionAction transitionAction)
655
{
656
   printf("                %s['", cleanName(stateName(state)));
657
   printText(&character, 1);
658
   printf("']: %s\n", cleanName(trActionName(transitionAction)));
659
}
660
#if !defined(RESIP_MSG_HEADER_SCANNER_DEBUG)
661
static const char* stateName(const char*)
662
{ return "RECOMPILE_WITH_SCANNER_DEBUG"; }
663
static const char* trActionName(const char*)
664
{ return stateName(0); }
665
#endif
666
/// START OF MEMBER METHODS
667
668
669
670
int
671
MsgHeaderScanner::dumpStateMachine(int fd)
672
{
673
   FILE *fp = fdopen(fd,"w");
674
   if (!fp) 
675
   {
676
      fprintf(stderr,"MsgHeaderScanner:: unable to open output file\n");
677
      return -1;
678
   }
679
   // Force instance so things are initialized -- YUCK! 
680
   MsgHeaderScanner scanner;(void)scanner;
681
   fprintf(fp,"digraph MsgHeaderScannerFSM {\n");
682
   fprintf(fp,"\tnode[shape=record\n\t\tfontsize=8\n\t\tfontname=\"Helvetica\"\n\t]\n");
683
   fprintf(fp,"\tedge [ fontsize=6 fontname=\"Helvetica\"]\n");
684
   
685
   fprintf(fp,"\tgraph [ ratio=0.8\n\t\tfontsize=6 compound=true ]");
686
   for(int state  = 0 ; state < numStates; ++state)
687
   {
688
      fprintf(fp,
689
              "  %s [ label = \"%d|%s\" ]\n",
690
              cleanName(stateName(state)),
691
              state,
692
              cleanName(stateName(state))
693
         );
694
      for(int category = 0 ; category < numCharCategories; ++category)
695
      {
696
         // Skip Verbose Error or Empty Transitions
697
         if (stateMachine[state][category].nextState == state &&
698
             (stateMachine[state][category].action == taError ||
699
              stateMachine[state][category].action == taNone
700
                )) continue;
701
              
702
         fprintf(fp,
703
                 "    %s -> %s [label=\"%s\\n%s\" ]\n",
704
                 cleanName(stateName(state)),
705
                 cleanName(stateName(stateMachine[state][category].nextState)),
706
                 categorySymbol(category),
707
                 cleanName(trActionName(stateMachine[state][category].action)));
708
      }
709
      fprintf(fp,"\n");
710
   }
711
   fprintf(fp,"}\n");
712
713
   return 0;
714
}
715
716
#endif //defined(RESIP_MSG_HEADER_SCANNER_DEBUG) 
717
718
719
720
#if defined(RESIP_MSG_HEADER_SCANNER_DEBUG)  
721
722
static const char *const multiValuedFieldNameArray[] = {
723
   "allow-events",
724
   "accept-encoding",
725
   "accept-language",
726
   "allow",
727
   "content-language",
728
   "proxy-require",
729
   "require",
730
   "supported",
731
   "subscription-state",
732
   "unsupported",
733
   "security-client",
734
   "security-server",
735
   "security-verify",
736
   "accept",
737
   "call-info",
738
   "alert-info",
739
   "error-info",
740
   "record-route",
741
   "route",
742
   "contact",
743
   "authorization",
744
   "proxy-authenticate",
745
   "proxy-authorization",
746
   "www-authenticate",
747
   "via",
748
   0
749
};
750
751
extern
752
void
753
lookupMsgHeaderFieldInfo(
754
   char *                             fieldName,               //inout
755
   unsigned int                       *fieldNameLength,        //inout
756
   MsgHeaderScanner::TextPropBitMask  fieldNameTextPropBitMask,
757
   int                                *fieldKind,              //out
758
   bool                               *isMultiValueAllowed)    //out
759
{
760
   *isMultiValueAllowed = false;
761
   const char *const *multiValuedFieldNamePtr = multiValuedFieldNameArray;
762
   for (;;)
763
   {
764
      const char *multiValuedFieldName = *multiValuedFieldNamePtr;
765
      if (!multiValuedFieldName) 
766
      {
767
         break;
768
      }
769
      if (strncmp(fieldName, multiValuedFieldName, *fieldNameLength) == 0) 
770
      {
771
         *isMultiValueAllowed = true;
772
         break;
773
      }
774
      ++multiValuedFieldNamePtr;
775
   }//for
776
}
777
778
static
779
bool
780
processMsgHeaderStatusLine(
781
   SipMessage *                       msg,
782
   char *                             lineText,
783
   unsigned int                       lineTextLength,
784
   MsgHeaderScanner::TextPropBitMask  lineTextPropBitMask)
785
{
786
   printf("status line: ");
787
   printText(lineText, lineTextLength);
788
   printf("\n");
789
   return true;
790
}
791
792
static
793
void
794
processMsgHeaderFieldNameAndValue(
795
   SipMessage *                       msg,
796
   int                                fieldKind,
797
   const char *                       fieldName,
798
   unsigned int                       fieldNameLength,
799
   char *                             valueText,
800
   unsigned int                       valueTextLength,
801
   MsgHeaderScanner::TextPropBitMask  valueTextPropBitMask)
802
{
803
   printText(fieldName, fieldNameLength);
804
   printf(": [[[[");
805
   printText(valueText, valueTextLength);
806
   printf("]]]]\n");
807
}
808
809
#else //!defined(RESIP_MSG_HEADER_SCANNER_DEBUG) } {
810
811
812
//   Determine a field's kind and whether it allows (comma separated) multi-values.
813
//   "fieldName" is not empty and contains only legal characters.
814
//   The text in "fieldName" may be canonicalized (eg, translating % escapes),
815
//   including shrinking it if necessary.
816
817
inline void
818
lookupMsgHeaderFieldInfo(char * fieldName,
819
                         unsigned int *fieldNameLength,   
820
                         MsgHeaderScanner::TextPropBitMask fieldNameTextPropBitMask,
821
                         int *fieldKind,             
822
                         bool *isMultiValueAllowed)    
823
1.28k
{
824
   //.jacob. Don't ignore fieldNameTextPropBitMask.
825
1.28k
   *fieldKind = Headers::getType(fieldName, *fieldNameLength);
826
1.28k
   *isMultiValueAllowed =
827
1.28k
      Headers::isCommaTokenizing(static_cast<Headers::Type>(*fieldKind));
828
1.28k
}
829
830
831
// "lineText" contains no carriage returns and no line feeds.
832
// Return true on success, false on failure.
833
834
inline bool
835
processMsgHeaderStatusLine(SipMessage * msg,
836
                           char * lineText,
837
                           unsigned int lineTextLength,
838
                           MsgHeaderScanner::TextPropBitMask lineTextPropBitMask)
839
575
{
840
   //.jacob. Don't ignore valueTextPropBitMask, and don't always return true.
841
575
   msg->setStartLine(lineText, lineTextLength);
842
575
   return true;
843
575
}
844
845
// This function is called once for a field with one value.  (The value could be
846
// several values, but separated by something other than commas.)
847
// This function is called once for a field with 0 comma-separated values, with
848
// an empty value.
849
// This function is called N times for a field with N comma-separated values,
850
// but with the same value of "fieldName" each time.
851
// "fieldName" is not empty and contains only legal characters.
852
// "valueText" may be empty, has no leading whitespace, may contain trailing
853
// whitespace, contains carriage returns and line feeds only in correct pairs
854
// and followed by whitespace, and, if the field is multi-valued, contains
855
// balanced '<'/'>' and '"' pairs, contains ',' only within '<'/'>' or '"'
856
// pairs, and respects '\\'s within '"' pairs.
857
// The text in "valueText" may be canonicalized (eg, translating % escapes),
858
// including shrinking it if necessary.
859
860
inline void
861
processMsgHeaderFieldNameAndValue(SipMessage * msg,
862
                                  int fieldKind,
863
                                  const char * fieldName,
864
                                  unsigned int fieldNameLength,
865
                                  char * valueText,
866
                                  unsigned int valueTextLength,
867
                                  MsgHeaderScanner::TextPropBitMask valueTextPropBitMask)
868
565k
{
869
   //.jacob. Don't ignore valueTextPropBitMask, particularly for '\r' & '\n'.
870
565k
   msg->addHeader(static_cast<Headers::Type>(fieldKind),
871
565k
                  fieldName,
872
565k
                  fieldNameLength,
873
565k
                  valueText,
874
565k
                  valueTextLength);
875
565k
}
876
877
#endif //!defined(RESIP_MSG_HEADER_SCANNER_DEBUG) }
878
879
bool MsgHeaderScanner::mInitialized = false;
880
881
MsgHeaderScanner::MsgHeaderScanner()
882
6.11k
{
883
6.11k
   if (!mInitialized)
884
1
   {
885
1
      mInitialized = true;
886
1
      initialize();
887
1
   }
888
6.11k
}
889
890
void
891
MsgHeaderScanner::prepareForMessage(SipMessage *  msg)
892
6.11k
{
893
6.11k
   mMsg = msg;
894
6.11k
   mState = sMsgStart;
895
6.11k
   mPrevScanChunkNumSavedTextChars = 0;
896
6.11k
   mNumHeaders=0;
897
6.11k
}
898
899
void
900
MsgHeaderScanner::prepareForFrag(SipMessage *  msg, bool hasStartLine)
901
0
{
902
0
   mMsg = msg;
903
0
   if (hasStartLine)
904
0
   {
905
0
      mState = sMsgStart;
906
0
   }
907
0
   else
908
0
   {
909
0
      mState = sAfterLineBreakAfterStatusLine;
910
0
   }
911
0
   mPrevScanChunkNumSavedTextChars = 0;
912
0
   mNumHeaders=0;
913
0
}
914
915
MsgHeaderScanner::ScanChunkResult
916
MsgHeaderScanner::scanChunk(char * chunk,
917
                            unsigned int chunkLength,
918
                            char ** unprocessedCharPtr)
919
6.11k
{
920
6.11k
   MsgHeaderScanner::ScanChunkResult result;
921
6.11k
   CharInfo* localCharInfoArray = charInfoArray;
922
6.11k
   TransitionInfo (*localStateMachine)[numCharCategories] = stateMachine;
923
6.11k
   State localState = mState;
924
6.11k
   char *charPtr = chunk + mPrevScanChunkNumSavedTextChars;
925
6.11k
   char *termCharPtr = chunk + chunkLength;
926
6.11k
   char saveChunkTermChar = *termCharPtr;
927
6.11k
   *termCharPtr = chunkTermSentinelChar;
928
6.11k
   char *textStartCharPtr;
929
6.11k
   MsgHeaderScanner::TextPropBitMask localTextPropBitMask = mTextPropBitMask;
930
6.11k
   if (mPrevScanChunkNumSavedTextChars == 0)
931
6.11k
   {
932
6.11k
      textStartCharPtr = 0;
933
6.11k
   }
934
0
   else
935
0
   {
936
0
      textStartCharPtr = chunk;
937
0
   }
938
6.11k
   --charPtr;  // The loop starts by advancing "charPtr", so pre-adjust it.
939
6.11k
   for (;;)
940
55.6M
   {
941
      // BEGIN message header character scan block BEGIN
942
      // The code in this block is executed once per message header character.
943
      // This entire file is designed specifically to minimize this block's size.
944
55.6M
      ++charPtr;
945
55.6M
      CharInfo *charInfo = &localCharInfoArray[((unsigned char) (*charPtr))];
946
55.6M
      CharCategory charCategory = charInfo->category;
947
55.6M
      localTextPropBitMask |= charInfo->textPropBitMask;
948
61.8M
     determineTransitionFromCharCategory:
949
61.8M
      TransitionInfo *transitionInfo =
950
61.8M
         &(localStateMachine[(unsigned)localState][(size_t)charCategory]);
951
61.8M
      TransitionAction transitionAction = transitionInfo->action;
952
#if defined(RESIP_MSG_HEADER_SCANNER_DEBUG)  
953
      printStateTransition(localState, *charPtr, transitionAction);
954
#endif
955
61.8M
      localState = transitionInfo->nextState;
956
61.8M
      if (transitionAction == taNone) continue;
957
      // END message header character scan block END
958
      // The loop remainder is executed about 4-5 times per message header line.
959
7.34M
      switch (transitionAction)
960
7.34M
      {
961
575
         case taTermStatusLine:
962
575
            if (!processMsgHeaderStatusLine(mMsg,
963
575
                                            textStartCharPtr,
964
575
                                            (unsigned int)(charPtr - textStartCharPtr),
965
575
                                            localTextPropBitMask))
966
0
            {
967
0
               result = MsgHeaderScanner::scrError;
968
0
               *unprocessedCharPtr = charPtr;
969
0
               goto endOfFunction;
970
0
            }
971
575
            textStartCharPtr = 0;
972
575
            break;
973
1.28k
         case taTermFieldName:
974
1.28k
         {
975
1.28k
            mFieldNameLength = (unsigned int)(charPtr - textStartCharPtr);
976
1.28k
            bool isMultiValueAllowed;
977
1.28k
            lookupMsgHeaderFieldInfo(textStartCharPtr,
978
1.28k
                                     &mFieldNameLength,
979
1.28k
                                     localTextPropBitMask,
980
1.28k
                                     &mFieldKind,
981
1.28k
                                     &isMultiValueAllowed);
982
1.28k
            mFieldName = textStartCharPtr;
983
1.28k
            textStartCharPtr = 0;
984
1.28k
            if (isMultiValueAllowed) 
985
432
            {
986
432
               localState += deltaOfNStateFrom1State;
987
432
            }
988
1.28k
         }
989
1.28k
         break;
990
737
         case taBeyondEmptyValue:
991
737
            processMsgHeaderFieldNameAndValue(mMsg,
992
737
                                              mFieldKind,
993
737
                                              mFieldName,
994
737
                                              mFieldNameLength,
995
737
                                              0,
996
737
                                              0,
997
737
                                              0);
998
737
            ++mNumHeaders;
999
737
            goto performStartTextAction;
1000
412
         case taTermValueAfterLineBreak:
1001
412
            processMsgHeaderFieldNameAndValue(mMsg,
1002
412
                                              mFieldKind,
1003
412
                                              mFieldName,
1004
412
                                              mFieldNameLength,
1005
412
                                              textStartCharPtr,
1006
412
                                              (unsigned int)((charPtr - textStartCharPtr) - 2),
1007
412
                                              localTextPropBitMask);       //^:CRLF
1008
412
            ++mNumHeaders;
1009
412
            goto performStartTextAction;
1010
564k
         case taTermValue:
1011
564k
            processMsgHeaderFieldNameAndValue(mMsg,
1012
564k
                                              mFieldKind,
1013
564k
                                              mFieldName,
1014
564k
                                              mFieldNameLength,
1015
564k
                                              textStartCharPtr,
1016
564k
                                              (unsigned int)(charPtr - textStartCharPtr),
1017
564k
                                              localTextPropBitMask);
1018
564k
            textStartCharPtr = 0;
1019
564k
            ++mNumHeaders;
1020
564k
            break;
1021
570k
         case taStartText:
1022
572k
        performStartTextAction:
1023
572k
            textStartCharPtr = charPtr;
1024
572k
            localTextPropBitMask = 0;
1025
572k
            break;
1026
153
         case taEndHeader:
1027
            // textStartCharPtr is not 0.  Not currently relevant.
1028
153
            result = MsgHeaderScanner::scrEnd;
1029
153
            *unprocessedCharPtr = charPtr + 1;  // The current char is processed.
1030
153
            goto endOfFunction;
1031
0
            break;
1032
6.20M
         case taChunkTermSentinel:
1033
6.20M
            if (charPtr == termCharPtr)
1034
3.17k
            {
1035
               // The chunk has been consumed.  Save some state and request another.
1036
3.17k
               mState = localState;
1037
3.17k
               if (textStartCharPtr == 0) 
1038
144
               {
1039
144
                  mPrevScanChunkNumSavedTextChars = 0;
1040
144
               }
1041
3.03k
               else
1042
3.03k
               {
1043
3.03k
                  mPrevScanChunkNumSavedTextChars = (unsigned int)(termCharPtr - textStartCharPtr);
1044
3.03k
               }
1045
3.17k
               mTextPropBitMask = localTextPropBitMask;
1046
3.17k
               result = MsgHeaderScanner::scrNextChunk;
1047
3.17k
               *unprocessedCharPtr = termCharPtr - mPrevScanChunkNumSavedTextChars;
1048
3.17k
               goto endOfFunction;
1049
3.17k
            }
1050
6.19M
            else
1051
6.19M
            {
1052
               // The character is not the sentinel.  Treat it like any other.
1053
6.19M
               charCategory = ccOther;
1054
6.19M
               goto determineTransitionFromCharCategory;
1055
6.19M
            }
1056
0
            break;
1057
2.78k
         default:
1058
2.78k
            result = MsgHeaderScanner::scrError;
1059
2.78k
            *unprocessedCharPtr = charPtr;
1060
2.78k
            goto endOfFunction;
1061
7.34M
      }//switch
1062
7.34M
   }//for
1063
6.11k
  endOfFunction:
1064
6.11k
   *termCharPtr = saveChunkTermChar;
1065
1066
6.11k
   if (result == MsgHeaderScanner::scrError && genericLogCheckLevel(resip::Log::Debug, RESIPROCATE_SUBSYSTEM))
1067
0
   {
1068
0
      const char* failedChar = *unprocessedCharPtr;
1069
0
      const char* lineBegin = failedChar;
1070
0
      while (lineBegin > chunk)
1071
0
      {
1072
0
         char c = *(lineBegin - 1);
1073
0
         if (!isprint(static_cast< unsigned char >(c)) && c != '\t')
1074
0
             break;
1075
0
         --lineBegin;
1076
0
      }
1077
0
      const char* lineEnd = failedChar;
1078
0
      while (lineEnd < termCharPtr)
1079
0
      {
1080
0
         char c = *lineEnd;
1081
0
         if (!isprint(static_cast< unsigned char >(c)) && c != '\t')
1082
0
             break;
1083
0
         ++lineEnd;
1084
0
      }
1085
1086
0
      resip::Log::Guard lg(resip::Log::Debug, RESIPROCATE_SUBSYSTEM, __FILE__, __LINE__, __func__);
1087
0
      EncodeStream& strm = lg.asStream();
1088
0
      strm << "Message header scanning failed at position " << (failedChar - chunk) << '\n';
1089
0
      strm.write(lineBegin, lineEnd - lineBegin);
1090
0
      strm.put('\n');
1091
0
      static const char filler[] = "          ";
1092
0
      unsigned int n = failedChar - lineBegin;
1093
0
      while (n > sizeof(filler) - 1)
1094
0
      {
1095
0
          strm.write(filler, sizeof(filler) - 1);
1096
0
          n -= sizeof(filler) - 1;
1097
0
      }
1098
0
      strm.write(filler, n);
1099
0
      strm << "^ (character codes:";
1100
0
      char buf[20];
1101
0
      char* p = buf;
1102
0
      auto print_hex = [&p](char c)
1103
0
      {
1104
0
          static const char hex_charset[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
1105
0
          *p++ = hex_charset[(static_cast< unsigned char >(c) >> 4u) & 0x0F];
1106
0
          *p++ = hex_charset[static_cast< unsigned char >(c) & 0x0F];
1107
0
      };
1108
0
      if ((failedChar - chunk) >= 2)
1109
0
      {
1110
0
          *p++ = ' ';
1111
0
          print_hex(*(failedChar - 2));
1112
0
      }
1113
0
      if ((failedChar - chunk) >= 1)
1114
0
      {
1115
0
          *p++ = ' ';
1116
0
          print_hex(*(failedChar - 1));
1117
0
      }
1118
0
      if ((termCharPtr - failedChar) >= 1)
1119
0
      {
1120
0
          *p++ = ' ';
1121
0
          *p++ = '[';
1122
0
          print_hex(*failedChar);
1123
0
          *p++ = ']';
1124
0
      }
1125
0
      if ((termCharPtr - failedChar) >= 2)
1126
0
      {
1127
0
          *p++ = ' ';
1128
0
          print_hex(*(failedChar + 1));
1129
0
      }
1130
0
      if ((termCharPtr - failedChar) >= 3)
1131
0
      {
1132
0
          *p++ = ' ';
1133
0
          print_hex(*(failedChar + 2));
1134
0
      }
1135
0
      *p++ = ')';
1136
0
      strm.write(buf, p - buf);
1137
0
   }
1138
1139
6.11k
   return result;
1140
6.11k
}
1141
1142
bool
1143
MsgHeaderScanner::initialize()
1144
1
{
1145
1
   initCharInfoArray();
1146
1
   initStateMachine();
1147
1
   return true;
1148
1
}
1149
1150
1151
} //namespace resip
1152
1153
1154
1155
#if defined(RESIP_MSG_HEADER_SCANNER_DEBUG) && defined(MSG_SCANNER_STANDALONE)
1156
1157
extern
1158
int
1159
main(unsigned int   numArgs,
1160
     const char * * argVector)
1161
{
1162
   ::resip::MsgHeaderScanner scanner;
1163
   scanner.prepareForMessage(0);
1164
   char *text =
1165
      "status\r\n"
1166
      "bobby: dummy\r\n"
1167
      "allow: foo, bar, \"don,\\\"xyz\r\n zy\", buzz\r\n\r\n";
1168
   unsigned int textLength = strlen(text);
1169
   char chunk[10000];
1170
   strcpy(chunk, text);
1171
   ::resip::MsgHeaderScanner::ScanChunkResult scanChunkResult;
1172
   char *unprocessedCharPtr;
1173
   scanChunkResult = scanner.scanChunk(chunk, 21, &unprocessedCharPtr);
1174
   if (scanChunkResult == ::resip::MsgHeaderScanner::scrNextChunk)
1175
   {
1176
      printf("Scanning another chunk '.");
1177
      ::resip::printText(unprocessedCharPtr, 1);
1178
      printf("'\n");
1179
      scanChunkResult =
1180
         scanner.scanChunk(unprocessedCharPtr,
1181
                           (chunk + textLength) - unprocessedCharPtr,
1182
                           &unprocessedCharPtr);
1183
   }
1184
   if (scanChunkResult != ::resip::MsgHeaderScanner::scrEnd)
1185
   {
1186
      printf("Error %d at character %d.\n",
1187
             scanChunkResult,
1188
             unprocessedCharPtr - chunk);
1189
   }
1190
   return 0;
1191
}
1192
1193
#endif //!defined(RESIP_MSG_HEADER_SCANNER_DEBUG) }
1194
1195
/* ====================================================================
1196
 * The Vovida Software License, Version 1.0 
1197
 * 
1198
 * Copyright (c) 2000-2005
1199
 * 
1200
 * Redistribution and use in source and binary forms, with or without
1201
 * modification, are permitted provided that the following conditions
1202
 * are met:
1203
 * 
1204
 * 1. Redistributions of source code must retain the above copyright
1205
 *    notice, this list of conditions and the following disclaimer.
1206
 * 
1207
 * 2. Redistributions in binary form must reproduce the above copyright
1208
 *    notice, this list of conditions and the following disclaimer in
1209
 *    the documentation and/or other materials provided with the
1210
 *    distribution.
1211
 * 
1212
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1213
 *    and "Vovida Open Communication Application Library (VOCAL)" must
1214
 *    not be used to endorse or promote products derived from this
1215
 *    software without prior written permission. For written
1216
 *    permission, please contact vocal@vovida.org.
1217
 *
1218
 * 4. Products derived from this software may not be called "VOCAL", nor
1219
 *    may "VOCAL" appear in their name, without prior written
1220
 *    permission of Vovida Networks, Inc.
1221
 * 
1222
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1223
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1224
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1225
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
1226
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1227
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1228
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1229
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1230
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1231
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1232
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1233
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1234
 * DAMAGE.
1235
 * 
1236
 * ====================================================================
1237
 * 
1238
 * This software consists of voluntary contributions made by Vovida
1239
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1240
 * Inc.  For more information on Vovida Networks, Inc., please see
1241
 * <http://www.vovida.org/>.
1242
 *
1243
 */