reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
MsgHeaderScanner.cxx
Go to the documentation of this file.
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>
12#include "rutil/Logger.hxx"
13
14#define RESIPROCATE_SUBSYSTEM Subsystem::SIP
15
16namespace resip
17{
18
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
25enum { chunkTermSentinelChar = '\0' };
26
43typedef char CharCategory;
44
45char*
47{
48 return new char[size + MaxNumCharsChunkOverflow];
49}
50
56
57static CharInfo charInfoArray[UCHAR_MAX+1];
58
59static inline int c2i(unsigned char c)
60{
61 return static_cast<int>(c);
62}
63
64static void initCharInfoArray()
65{
66 for(unsigned int charIndex = 0; charIndex <= UCHAR_MAX; ++charIndex)
67 {
68 charInfoArray[charIndex].category = ccOther;
69 charInfoArray[charIndex].textPropBitMask = 0;
70 }
71
72 for(const char *charPtr = "abcdefghijklmnopqrstuvwxyz"
73 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.!%*_+`'~";
74 *charPtr;
75 ++charPtr)
76 {
78 }
79
90 // Assert: "chunkTermSentinelChar"'s category is still the default "ccOther".
92 // Init text property bit masks.
111}
112
114// States marked '1' scan normal values. States marked 'N' scan multi-values.
115
148
149typedef char State;
150
151// For each '1' state, the 'N' state is "deltaOfNStateFrom1State" larger.
153
155
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.
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.
176typedef char TransitionAction;
177
178
184
186
187inline void specTransition(State state,
188 CharCategory charCategory,
189 TransitionAction action,
190 State nextState)
191{
192 stateMachine[c2i(state)][c2i(charCategory)].action = action;
193 stateMachine[c2i(state)][c2i(charCategory)].nextState = nextState;
194}
195
196static void specDefaultTransition(State state,
197 TransitionAction action,
198 State nextState)
199{
200 for (int charCategory = 0;
201 charCategory < numCharCategories;
202 ++charCategory)
203 {
204 specTransition(state, charCategory, action, nextState);
205 }
207 specTransition(state, ccLineFeed, taError, state);
209}
210
211static void specHalfLineBreakState(State halfLineBreakState,
212 State afterLineBreakState)
213{
214 specDefaultTransition(halfLineBreakState, taError, halfLineBreakState);
215 specTransition(halfLineBreakState, ccLineFeed, taNone, afterLineBreakState);
216}
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
223static void specXValueStates(int stateDelta)
224{
226 taError,
227 sScanWhitespaceAfter1FieldName + stateDelta);
230 taNone,
231 sScanWhitespaceAfter1FieldName + stateDelta);
233 ccColon,
234 taNone,
235 sScanWhitespaceOr1Value + stateDelta);
238 sScan1Value + stateDelta);
241 taNone,
242 sScanWhitespaceOr1Value + stateDelta);
243 if (stateDelta == deltaOfNStateFrom1State)
244 {
246 ccComma,
247 taError,
248 sScanWhitespaceOr1Value + stateDelta);
257 }
260 taNone,
265 taError,
273 taNone,
274 sScanWhitespaceOr1Value + stateDelta);
280 taNone,
281 sScan1Value + stateDelta);
282 if (stateDelta == deltaOfNStateFrom1State)
283 {
284 specTransition(sScan1Value + stateDelta,
285 ccComma,
287 sScanWhitespaceOr1Value + stateDelta);
288 specTransition(sScan1Value + stateDelta,
290 taNone,
292 specTransition(sScan1Value + stateDelta,
294 taNone,
296 }
297 specTransition(sScan1Value + stateDelta,
299 taNone,
300 sHalfLineBreakIn1Value + stateDelta);
302 sAfterLineBreakIn1Value + stateDelta);
304 taError,
305 sAfterLineBreakIn1Value + stateDelta);
312 taNone,
313 sScan1Value + stateDelta);
318}
319
320static void initStateMachine()
321{
322 // By convention, error transitions maintain the same state.
326 taNone,
338 taError,
346 taError,
350 taNone,
359 ccColon,
367 taNone,
371 taNone,
375 taNone,
378 taNone,
383 taError,
387 taNone,
392 taNone,
396 taNone,
401 taError,
405 taNone,
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
415 sMsgStart); // Arbitrary but possibly handy.
416}
417
418// Debug follows
419#if defined(RESIP_MSG_HEADER_SCANNER_DEBUG)
420
421static 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
445static const char *
446categorySymbol(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
466static const char *
467categoryName(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
487static const char *
488cleanName(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
513static const char *
514stateName(State state)
515{
516 const char *stateName;
517 switch (state)
518 {
519 case sMsgStart:
520 stateName = "sMsgStart";
521 break;
523 stateName = "sHalfLineBreakAtMsgStart";
524 break;
525 case sScanStatusLine:
526 stateName = "sScanStatusLine";
527 break;
529 stateName = "sHalfLineBreakAfterStatusLine";
530 break;
532 stateName = "sAfterLineBreakAfterStatusLine";
533 break;
534 case sScanFieldName:
535 stateName = "sScanFieldName";
536 break;
538 stateName = "sScanWhitespaceAfter1FieldName";
539 break;
541 stateName = "sScanWhitespaceAfterNFieldName";
542 break;
544 stateName = "sScanWhitespaceOr1Value";
545 break;
547 stateName = "sScanWhitespaceOrNValue";
548 break;
550 stateName = "sHalfLineBreakInWhitespaceBefore1Value";
551 break;
553 stateName = "sHalfLineBreakInWhitespaceBeforeNValue";
554 break;
556 stateName = "sAfterLineBreakInWhitespaceBefore1Value";
557 break;
559 stateName = "sAfterLineBreakInWhitespaceBeforeNValue";
560 break;
561 case sScan1Value:
562 stateName = "sScan1Value";
563 break;
564 case sScanNValue:
565 stateName = "sScanNValue";
566 break;
568 stateName = "sHalfLineBreakIn1Value";
569 break;
571 stateName = "sHalfLineBreakInNValue";
572 break;
574 stateName = "sAfterLineBreakIn1Value";
575 break;
577 stateName = "sAfterLineBreakInNValue";
578 break;
580 stateName = "sScanNValueInQuotes";
581 break;
583 stateName = "sAfterEscCharInQuotesInNValue";
584 break;
586 stateName = "sHalfLineBreakInQuotesInNValue";
587 break;
589 stateName = "sAfterLineBreakInQuotesInNValue";
590 break;
592 stateName = "sScanNValueInAngles";
593 break;
595 stateName = "sHalfLineBreakInAnglesInNValue";
596 break;
598 stateName = "sAfterLineBreakInAnglesInNValue";
599 break;
601 stateName = "sHalfLineBreakAfterLineBreak";
602 break;
603 default:
604 stateName = "<unknown>";
605 }//switch
606 return stateName;
607}
608
609static const char *
610trActionName(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;
625 transitionActionName = "taBeyondEmptyValue";
626 break;
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;
640 transitionActionName = "taChunkTermSentinel";
641 break;
642 case taError:
643 transitionActionName = "taError";
644 break;
645 default:
646 transitionActionName = "<unknown>";
647 }
648 return transitionActionName;
649}
650
651static void
652printStateTransition(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)
661static const char* stateName(const char*)
662{ return "RECOMPILE_WITH_SCANNER_DEBUG"; }
663static const char* trActionName(const char*)
664{ return stateName(0); }
665#endif
667
668
669
670int
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
722static 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
751extern
752void
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
778static
779bool
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
792static
793void
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
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
817inline void
819 unsigned int *fieldNameLength,
820 MsgHeaderScanner::TextPropBitMask fieldNameTextPropBitMask,
821 int *fieldKind,
822 bool *isMultiValueAllowed)
823{
824 //.jacob. Don't ignore fieldNameTextPropBitMask.
825 *fieldKind = Headers::getType(fieldName, *fieldNameLength);
826 *isMultiValueAllowed =
827 Headers::isCommaTokenizing(static_cast<Headers::Type>(*fieldKind));
828}
829
830
831// "lineText" contains no carriage returns and no line feeds.
832// Return true on success, false on failure.
833
834inline bool
836 char * lineText,
837 unsigned int lineTextLength,
838 MsgHeaderScanner::TextPropBitMask lineTextPropBitMask)
839{
840 //.jacob. Don't ignore valueTextPropBitMask, and don't always return true.
841 msg->setStartLine(lineText, lineTextLength);
842 return true;
843}
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
860inline void
862 int fieldKind,
863 const char * fieldName,
864 unsigned int fieldNameLength,
865 char * valueText,
866 unsigned int valueTextLength,
867 MsgHeaderScanner::TextPropBitMask valueTextPropBitMask)
868{
869 //.jacob. Don't ignore valueTextPropBitMask, particularly for '\r' & '\n'.
870 msg->addHeader(static_cast<Headers::Type>(fieldKind),
871 fieldName,
872 fieldNameLength,
873 valueText,
874 valueTextLength);
875}
876
877#endif
878
879bool MsgHeaderScanner::mInitialized = false;
880
882{
883 if (!mInitialized)
884 {
885 mInitialized = true;
886 initialize();
887 }
888}
889
890void
892{
893 mMsg = msg;
894 mState = sMsgStart;
895 mPrevScanChunkNumSavedTextChars = 0;
896 mNumHeaders=0;
897}
898
899void
901{
902 mMsg = msg;
903 if (hasStartLine)
904 {
905 mState = sMsgStart;
906 }
907 else
908 {
910 }
911 mPrevScanChunkNumSavedTextChars = 0;
912 mNumHeaders=0;
913}
914
917 unsigned int chunkLength,
918 char ** unprocessedCharPtr)
919{
921 CharInfo* localCharInfoArray = charInfoArray;
922 TransitionInfo (*localStateMachine)[numCharCategories] = stateMachine;
923 State localState = mState;
924 char *charPtr = chunk + mPrevScanChunkNumSavedTextChars;
925 char *termCharPtr = chunk + chunkLength;
926 char saveChunkTermChar = *termCharPtr;
927 *termCharPtr = chunkTermSentinelChar;
928 char *textStartCharPtr;
929 MsgHeaderScanner::TextPropBitMask localTextPropBitMask = mTextPropBitMask;
930 if (mPrevScanChunkNumSavedTextChars == 0)
931 {
932 textStartCharPtr = 0;
933 }
934 else
935 {
936 textStartCharPtr = chunk;
937 }
938 for (;; ++charPtr)
939 {
940 // BEGIN message header character scan block BEGIN
941 // The code in this block is executed once per message header character.
942 // This entire file is designed specifically to minimize this block's size.
943 CharInfo *charInfo = &localCharInfoArray[((unsigned char) (*charPtr))];
944 CharCategory charCategory = charInfo->category;
945 localTextPropBitMask |= charInfo->textPropBitMask;
946 determineTransitionFromCharCategory:
947 TransitionInfo *transitionInfo =
948 &(localStateMachine[(unsigned)localState][(size_t)charCategory]);
949 TransitionAction transitionAction = transitionInfo->action;
950#if defined(RESIP_MSG_HEADER_SCANNER_DEBUG)
951 printStateTransition(localState, *charPtr, transitionAction);
952#endif
953 localState = transitionInfo->nextState;
954 if (transitionAction == taNone) continue;
955 // END message header character scan block END
956 // The loop remainder is executed about 4-5 times per message header line.
957 switch (transitionAction)
958 {
959 case taTermStatusLine:
961 textStartCharPtr,
962 (unsigned int)(charPtr - textStartCharPtr),
963 localTextPropBitMask))
964 {
966 *unprocessedCharPtr = charPtr;
967 goto endOfFunction;
968 }
969 textStartCharPtr = 0;
970 break;
971 case taTermFieldName:
972 {
973 mFieldNameLength = (unsigned int)(charPtr - textStartCharPtr);
974 bool isMultiValueAllowed;
975 lookupMsgHeaderFieldInfo(textStartCharPtr,
976 &mFieldNameLength,
977 localTextPropBitMask,
978 &mFieldKind,
979 &isMultiValueAllowed);
980 mFieldName = textStartCharPtr;
981 textStartCharPtr = 0;
982 if (isMultiValueAllowed)
983 {
984 localState += deltaOfNStateFrom1State;
985 }
986 }
987 break;
990 mFieldKind,
991 mFieldName,
992 mFieldNameLength,
993 charPtr,
994 0,
995 0);
996 ++mNumHeaders;
997 goto performStartTextAction;
1000 mFieldKind,
1001 mFieldName,
1002 mFieldNameLength,
1003 textStartCharPtr,
1004 (unsigned int)((charPtr - textStartCharPtr) - 2),
1005 localTextPropBitMask); //^:CRLF
1006 ++mNumHeaders;
1007 goto performStartTextAction;
1008 case taTermValue:
1010 mFieldKind,
1011 mFieldName,
1012 mFieldNameLength,
1013 textStartCharPtr,
1014 (unsigned int)(charPtr - textStartCharPtr),
1015 localTextPropBitMask);
1016 textStartCharPtr = 0;
1017 ++mNumHeaders;
1018 break;
1019 case taStartText:
1020 performStartTextAction:
1021 textStartCharPtr = charPtr;
1022 localTextPropBitMask = 0;
1023 break;
1024 case taEndHeader:
1025 // textStartCharPtr is not 0. Not currently relevant.
1026 result = MsgHeaderScanner::scrEnd;
1027 *unprocessedCharPtr = charPtr + 1; // The current char is processed.
1028 goto endOfFunction;
1029 break;
1031 if (charPtr == termCharPtr)
1032 {
1033 // The chunk has been consumed. Save some state and request another.
1034 mState = localState;
1035 if (textStartCharPtr == 0)
1036 {
1037 mPrevScanChunkNumSavedTextChars = 0;
1038 }
1039 else
1040 {
1041 mPrevScanChunkNumSavedTextChars = (unsigned int)(termCharPtr - textStartCharPtr);
1042 }
1043 mTextPropBitMask = localTextPropBitMask;
1045 *unprocessedCharPtr = termCharPtr - mPrevScanChunkNumSavedTextChars;
1046 goto endOfFunction;
1047 }
1048 else
1049 {
1050 // The character is not the sentinel. Treat it like any other.
1051 charCategory = ccOther;
1052 goto determineTransitionFromCharCategory;
1053 }
1054 break;
1055 default:
1057 *unprocessedCharPtr = charPtr;
1058 goto endOfFunction;
1059 }//switch
1060 }//for
1061 endOfFunction:
1062 *termCharPtr = saveChunkTermChar;
1063
1065 {
1066 const char* failedChar = *unprocessedCharPtr;
1067 const char* lineBegin = failedChar;
1068 while (lineBegin > chunk)
1069 {
1070 char c = *(lineBegin - 1);
1071 if (!isprint(static_cast< unsigned char >(c)) && c != '\t')
1072 break;
1073 --lineBegin;
1074 }
1075 const char* lineEnd = failedChar;
1076 while (lineEnd < termCharPtr)
1077 {
1078 char c = *lineEnd;
1079 if (!isprint(static_cast< unsigned char >(c)) && c != '\t')
1080 break;
1081 ++lineEnd;
1082 }
1083
1084 resip::Log::Guard lg(resip::Log::Debug, RESIPROCATE_SUBSYSTEM, __FILE__, __LINE__, __func__);
1085 EncodeStream& strm = lg.asStream();
1086 strm << "Message header scanning failed at position " << (failedChar - chunk) << '\n';
1087 strm.write(lineBegin, lineEnd - lineBegin);
1088 strm.put('\n');
1089 static const char filler[] = " ";
1090 unsigned int n = (unsigned int)(failedChar - lineBegin);
1091 while (n > sizeof(filler) - 1)
1092 {
1093 strm.write(filler, sizeof(filler) - 1);
1094 n -= sizeof(filler) - 1;
1095 }
1096 strm.write(filler, n);
1097 strm << "^ (character codes:";
1098 char buf[20];
1099 char* p = buf;
1100 auto print_hex = [&p](char c)
1101 {
1102 static const char hex_charset[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
1103 *p++ = hex_charset[(static_cast< unsigned char >(c) >> 4u) & 0x0F];
1104 *p++ = hex_charset[static_cast< unsigned char >(c) & 0x0F];
1105 };
1106 if ((failedChar - chunk) >= 2)
1107 {
1108 *p++ = ' ';
1109 print_hex(*(failedChar - 2));
1110 }
1111 if ((failedChar - chunk) >= 1)
1112 {
1113 *p++ = ' ';
1114 print_hex(*(failedChar - 1));
1115 }
1116 if ((termCharPtr - failedChar) >= 1)
1117 {
1118 *p++ = ' ';
1119 *p++ = '[';
1120 print_hex(*failedChar);
1121 *p++ = ']';
1122 }
1123 if ((termCharPtr - failedChar) >= 2)
1124 {
1125 *p++ = ' ';
1126 print_hex(*(failedChar + 1));
1127 }
1128 if ((termCharPtr - failedChar) >= 3)
1129 {
1130 *p++ = ' ';
1131 print_hex(*(failedChar + 2));
1132 }
1133 *p++ = ')';
1134 strm.write(buf, p - buf);
1135 }
1136
1137 return result;
1138}
1139
1140bool
1141MsgHeaderScanner::initialize()
1142{
1145 return true;
1146}
1147
1148
1149} //namespace resip
1150
1151
1152
1153#if defined(RESIP_MSG_HEADER_SCANNER_DEBUG) && defined(MSG_SCANNER_STANDALONE)
1154
1155extern
1156int
1157main(unsigned int numArgs,
1158 const char * * argVector)
1159{
1162 char *text =
1163 "status\r\n"
1164 "bobby: dummy\r\n"
1165 "allow: foo, bar, \"don,\\\"xyz\r\n zy\", buzz\r\n\r\n";
1166 unsigned int textLength = strlen(text);
1167 char chunk[10000];
1168 strcpy(chunk, text);
1170 char *unprocessedCharPtr;
1173 {
1174 printf("Scanning another chunk '.");
1175 ::resip::printText(unprocessedCharPtr, 1);
1176 printf("'\n");
1178 scanner.scanChunk(unprocessedCharPtr,
1181 }
1183 {
1184 printf("Error %d at character %d.\n",
1187 }
1188 return 0;
1189}
1190
1191#endif
1192
1193/* ====================================================================
1194 * The Vovida Software License, Version 1.0
1195 *
1196 * Copyright (c) 2000-2005
1197 *
1198 * Redistribution and use in source and binary forms, with or without
1199 * modification, are permitted provided that the following conditions
1200 * are met:
1201 *
1202 * 1. Redistributions of source code must retain the above copyright
1203 * notice, this list of conditions and the following disclaimer.
1204 *
1205 * 2. Redistributions in binary form must reproduce the above copyright
1206 * notice, this list of conditions and the following disclaimer in
1207 * the documentation and/or other materials provided with the
1208 * distribution.
1209 *
1210 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1211 * and "Vovida Open Communication Application Library (VOCAL)" must
1212 * not be used to endorse or promote products derived from this
1213 * software without prior written permission. For written
1214 * permission, please contact vocal@vovida.org.
1215 *
1216 * 4. Products derived from this software may not be called "VOCAL", nor
1217 * may "VOCAL" appear in their name, without prior written
1218 * permission of Vovida Networks, Inc.
1219 *
1220 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1221 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1222 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1223 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1224 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1225 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1226 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1227 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1228 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1229 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1230 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1231 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1232 * DAMAGE.
1233 *
1234 * ====================================================================
1235 *
1236 * This software consists of voluntary contributions made by Vovida
1237 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1238 * Inc. For more information on Vovida Networks, Inc., please see
1239 * <http://www.vovida.org/>.
1240 *
1241 */
#define RESIPROCATE_SUBSYSTEM
static bool genericLogCheckLevel(resip::Log::Level level, const resip::Subsystem &sub)
Definition Logger.hxx:144
int main(int argc, char **argv)
Definition adig.c:209
static Type getType(const char *name, size_t len)
Definition Headers.cxx:351
static bool isCommaTokenizing(Type type)
Definition Headers.cxx:25
Implementation for logging macros.
Definition Log.hxx:106
EncodeStream & asStream()
Definition Log.hxx:119
@ Debug
Definition Log.hxx:84
void prepareForMessage(SipMessage *msg)
static int dumpStateMachine(int fd)
void prepareForFrag(SipMessage *msg, bool hasStartLine)
MsgHeaderScanner::ScanChunkResult scanChunk(char *chunk, unsigned int chunkLength, char **unprocessedCharPtr)
static char * allocateBuffer(size_t size)
Represents a SIP message.
void setStartLine(const char *start, uint32_t len)
void addHeader(Headers::Type header, const char *headerName, uint32_t headerNameLen, const char *value, uint32_t valuelen)
Add HeaderFieldValue given enum, header name and value.
dcm! – add UnusedChecking(_enum) below;
void processMsgHeaderFieldNameAndValue(SipMessage *msg, int fieldKind, const char *fieldName, unsigned int fieldNameLength, char *valueText, unsigned int valueTextLength, MsgHeaderScanner::TextPropBitMask valueTextPropBitMask)
static void initStateMachine()
static void specHalfLineBreakState(State halfLineBreakState, State afterLineBreakState)
@ taTermValueAfterLineBreak
@ ccDoubleQuotationMark
@ sAfterLineBreakInAnglesInNValue
@ sHalfLineBreakAfterStatusLine
@ sAfterLineBreakInQuotesInNValue
@ sHalfLineBreakAtMsgStart
@ sAfterLineBreakInWhitespaceBeforeNValue
@ sHalfLineBreakAfterLineBreak
@ sAfterLineBreakAfterStatusLine
@ sHalfLineBreakInQuotesInNValue
@ sScanWhitespaceOrNValue
@ sHalfLineBreakInWhitespaceBefore1Value
@ sAfterLineBreakInWhitespaceBefore1Value
@ sAfterEscCharInQuotesInNValue
@ sScanWhitespaceAfter1FieldName
@ sAfterLineBreakInNValue
@ sScanWhitespaceOr1Value
@ sAfterLineBreakIn1Value
@ sScanWhitespaceAfterNFieldName
@ sHalfLineBreakInWhitespaceBeforeNValue
@ sHalfLineBreakInAnglesInNValue
static void specDefaultTransition(State state, TransitionAction action, State nextState)
char TransitionAction
bool processMsgHeaderStatusLine(SipMessage *msg, char *lineText, unsigned int lineTextLength, MsgHeaderScanner::TextPropBitMask lineTextPropBitMask)
static void specXValueStates(int stateDelta)
static void initCharInfoArray()
static TransitionInfo stateMachine[numStates][numCharCategories]
static int c2i(unsigned char c)
void lookupMsgHeaderFieldInfo(char *fieldName, unsigned int *fieldNameLength, MsgHeaderScanner::TextPropBitMask fieldNameTextPropBitMask, int *fieldKind, bool *isMultiValueAllowed)
defined(RESIP_MSG_HEADER_SCANNER_DEBUG) } {
@ chunkTermSentinelChar
static CharInfo charInfoArray[UCHAR_MAX+1]
char CharCategory
void specTransition(State state, CharCategory charCategory, TransitionAction action, State nextState)
@ deltaOfNStateFrom1State
#define EncodeStream
CharCategory category
MsgHeaderScanner::TextPropBitMask textPropBitMask
void list()
Definition userAdmin.cxx:66