00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef __GNUG__
00024 # pragma implementation "TelnetParser.h"
00025 #endif
00026
00027 #include "StrMod/TelnetParser.h"
00028 #include "StrMod/TelnetChunkBuilder.h"
00029 #include "StrMod/TelnetChars.h"
00030 #include "StrMod/LocalCopy.h"
00031 #include "StrMod/PreAllocBuffer.h"
00032 #include "StrMod/StrChunkPtrT.h"
00033 #include <cassert>
00034 #include <cstddef>
00035
00036 namespace strmod {
00037 namespace strmod {
00038
00039 const STR_ClassIdent TelnetParser::identifier(50UL);
00040
00041 TelnetParser::TelnetParser()
00042 : curpos_(0), state_(PS_Normal), regionbegin_(0), regionend_(0)
00043 {
00044 }
00045
00046 TelnetParser::~TelnetParser()
00047 {
00048 if (cooked_)
00049 {
00050 delete cooked_;
00051 }
00052 }
00053
00054 inline void TelnetParser::stateNormal(ParserState &state, U1Byte ch)
00055 {
00056 if (ch == TelnetChars::IAC)
00057 {
00058 state = PS_Escape;
00059 }
00060 }
00061
00062 inline void TelnetParser::stateEscape(ParserState &state,
00063 const U1Byte ch, size_t i,
00064 TelnetChunkBuilder &builder)
00065 {
00066 if (ch == TelnetChars::IAC)
00067 {
00068 regionend_ = i;
00069
00070 builder.addDataBlock(regionbegin_, regionend_);
00071 regionbegin_ = i + 1;
00072 state = PS_Normal;
00073 }
00074 if (ch == TelnetChars::SB)
00075 {
00076 state = PS_SuboptNum;
00077 }
00078 else if (TelnetChars::convertCharToOptionNegotiation(ch, negtype_))
00079 {
00080 state = PS_SubNeg;
00081 }
00082 else
00083 {
00084 TelnetChars::Commands cmd;
00085
00086 if (TelnetChars::convertCharToCommand(ch, cmd))
00087 {
00088
00089 regionend_ = i - 1;
00090 assert(regionend_ >= regionbegin_);
00091 if (regionend_ > regionbegin_)
00092 {
00093 builder.addDataBlock(regionbegin_, regionend_);
00094 }
00095 builder.addCharCommand(cmd);
00096
00097 regionbegin_ = i + 1;
00098 state = PS_Normal;
00099 }
00100 else
00101 {
00102 state = PS_Normal;
00103
00104
00105
00106 }
00107 }
00108 }
00109
00110 inline void TelnetParser::stateSubNeg(ParserState &state,
00111 const U1Byte ch, size_t i,
00112 TelnetChunkBuilder &builder)
00113 {
00114
00115
00116
00117 regionend_ = i - 2;
00118 assert(regionend_ >= regionbegin_);
00119 if (regionend_ > regionbegin_)
00120 {
00121 builder.addDataBlock(regionbegin_, regionend_);
00122 }
00123 builder.addNegotiationCommand(negtype_, ch);
00124 regionbegin_ = i + 1;
00125 state = PS_Normal;
00126 }
00127
00128 inline void TelnetParser::stateSuboptNum(ParserState &state,
00129 const U1Byte ch, size_t i,
00130 TelnetChunkBuilder &builder)
00131 {
00132
00133
00134 regionend_ = i - 2;
00135 assert(regionend_ >= regionbegin_);
00136 assert(curpos_ >= regionbegin_);
00137 if (regionend_ > regionbegin_)
00138 {
00139 builder.addDataBlock(regionbegin_, regionend_);
00140 }
00141 if (ch != 255)
00142 {
00143
00144 subopt_type_ = ch;
00145 cooked_ = new PreAllocBuffer<48>;
00146 cooked_->resize(48);
00147 cookedbuf_ = cooked_->getCharP();
00148 cooked_total_ = 48;
00149 cooked_used_ = 0;
00150 state = PS_Subopt;
00151 }
00152 else
00153 {
00154
00155
00156
00157
00158 state = PS_Normal;
00159 }
00160
00161
00162 regionbegin_ = i + 1;
00163 }
00164
00165 inline void TelnetParser::stateSubopt(ParserState &state, U1Byte ch)
00166 {
00167 if (ch == TelnetChars::IAC)
00168 {
00169 state = PS_SuboptEscape;
00170 }
00171 else
00172 {
00173 if (cooked_used_ >= cooked_total_)
00174 {
00175 cooked_->resize(cooked_used_ + cooked_used_ / 2);
00176 cookedbuf_ = cooked_->getCharP();
00177 cooked_total_ = cooked_->Length();
00178 assert(cooked_total_ > cooked_used_);
00179 }
00180 cookedbuf_[cooked_used_++] = ch;
00181 }
00182 }
00183
00184 inline void TelnetParser::stateSuboptEscape(ParserState &state,
00185 U1Byte ch, size_t i,
00186 TelnetChunkBuilder &builder)
00187 {
00188 if (ch == TelnetChars::SE)
00189 {
00190
00191
00192
00193
00194 regionend_ = i - 1;
00195 cooked_->resize(cooked_used_);
00196 {
00197 StrChunkPtrT<BufferChunk> tmp(cooked_);
00198 builder.addSuboption(subopt_type_, regionbegin_, regionend_, tmp);
00199 }
00200 cooked_ = 0;
00201 regionbegin_ = i + 1;
00202 state = PS_Normal;
00203 }
00204 else
00205 {
00206 state = PS_Subopt;
00207 if ((cooked_used_ + 1) >= cooked_total_)
00208 {
00209 cooked_->resize(cooked_used_ + cooked_used_ / 2);
00210 cookedbuf_ = cooked_->getCharP();
00211 cooked_total_ = cooked_->Length();
00212 assert(cooked_total_ > (cooked_used_ + 1));
00213 }
00214 if (ch != TelnetChars::IAC)
00215 {
00216 cookedbuf_[cooked_used_++] = TelnetChars::IAC;
00217 }
00218 cookedbuf_[cooked_used_++] = ch;
00219 }
00220 }
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237 void TelnetParser::processData(const void *data, size_t len,
00238 TelnetChunkBuilder &builder)
00239 {
00240 assert((state_ == PS_Normal) || (state_ == PS_Escape)
00241 || (state_ == PS_SubNeg) || (state_ == PS_SuboptNum)
00242 || (state_ == PS_Subopt) || (state_ == PS_SuboptEscape));
00243
00244
00245 const U1Byte *chars = static_cast<const U1Byte *>(data);
00246 const size_t dataend = curpos_ + len;
00247 LocalCopy<ParserState> state(state_);
00248
00249 for (size_t i = curpos_; i < dataend; ++i)
00250 {
00251 const U1Byte ch = *chars++;
00252 switch (state.local)
00253 {
00254 case PS_Normal:
00255 stateNormal(state.local, ch);
00256 break;
00257 case PS_Escape:
00258 stateEscape(state.local, ch, i, builder);
00259 break;
00260 case PS_SubNeg:
00261 stateSubNeg(state.local, ch, i, builder);
00262 break;
00263 case PS_SuboptNum:
00264 stateSuboptNum(state.local, ch, i, builder);
00265 break;
00266 case PS_Subopt:
00267 stateSubopt(state.local, ch);
00268 break;
00269 case PS_SuboptEscape:
00270 stateSuboptEscape(state.local, ch, i, builder);
00271 break;
00272
00273 default:
00274
00275 assert(false);
00276
00277 reset(builder);
00278 break;
00279 }
00280 }
00281 curpos_ += len;
00282 }
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295 void TelnetParser::endOfChunk(TelnetChunkBuilder &builder)
00296 {
00297 switch (state_)
00298 {
00299 case PS_Normal:
00300 regionend_ = curpos_;
00301 builder.addDataBlock(regionbegin_, regionend_);
00302 regionbegin_ = regionend_;
00303 break;
00304 case PS_Escape:
00305
00306 assert(curpos_ >= 1);
00307 regionend_ = curpos_ - 1;
00308 assert(regionend_ >= regionbegin_);
00309 if (regionend_ > regionbegin_)
00310 {
00311 builder.addDataBlock(regionbegin_, regionend_);
00312 }
00313 regionbegin_ = regionend_;
00314 break;
00315 case PS_SuboptNum:
00316 case PS_SubNeg:
00317
00318
00319 assert(curpos_ >= 2);
00320 regionend_ = curpos_ - 2;
00321 assert(regionend_ >= regionbegin_);
00322 if (regionend_ > regionbegin_)
00323 {
00324 builder.addDataBlock(regionbegin_, regionend_);
00325 }
00326 regionbegin_ = regionend_;
00327 }
00328 }
00329
00330 void TelnetParser::reset(TelnetChunkBuilder &builder)
00331 {
00332 if ((state_ == PS_Subopt) || (state_ == PS_SuboptEscape))
00333 {
00334
00335 regionend_ = curpos_;
00336 cooked_->resize(cooked_used_);
00337 {
00338 StrChunkPtrT<BufferChunk> tmp(cooked_);
00339 builder.addSuboption(subopt_type_, regionbegin_, regionend_, tmp);
00340 }
00341 cooked_ = 0;
00342 }
00343 regionbegin_ = curpos_;
00344 state_ = PS_Normal;
00345 }
00346
00347 };
00348 };