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