Adjustment and fixes for boost-1.36 support
[senf.git] / Utils / Termlib / Telnet.cc
1 // $Id$
2 //
3 // Copyright (C) 2008 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Telnet non-inline non-template implementation */
25
26 #include "Telnet.hh"
27 //#include "Telnet.ih"
28
29 // Custom includes
30 #include "../membind.hh"
31 #include "../Logger/SenfLog.hh"
32
33 //#include "Telnet.mpp"
34 #define prefix_
35 ///////////////////////////////cc.p////////////////////////////////////////
36
37 prefix_ senf::term::BaseTelnetProtocol::BaseTelnetProtocol(Handle handle)
38     : handle_ (handle), charState_ (NORMAL), command_ (CMD_NONE), option_ (0),
39       inputEvent_ ("senf::term::BaseTelnetProtocol::input",
40                    senf::membind(&BaseTelnetProtocol::readHandler, this), handle, 
41                    senf::scheduler::FdEvent::EV_READ),
42       outputEvent_ ("senf::term::BaseTelnetProtocol::output",
43                     senf::membind(&BaseTelnetProtocol::writeHandler, this), handle,
44                     senf::scheduler::FdEvent::EV_WRITE, false),
45       pendingRequests_ (0u),
46       requestTimeout_ (ClockService::milliseconds(DEFAULT_REQUEST_TIMEOUT_MS)),
47       timeout_ ("senf::term::BaseTelnetProtocol::configTimeout",
48                 senf::membind(&BaseTelnetProtocol::timeout, this))
49 {}
50
51 prefix_ senf::term::BaseTelnetProtocol::BaseTelnetProtocol()
52     : handle_ (), charState_ (NORMAL), command_ (CMD_NONE), option_ (0),
53       inputEvent_ ("senf::term::BaseTelnetProtocol::input", 0),
54       outputEvent_ ("senf::term::BaseTelnetProtocol::output", 0),
55       pendingRequests_ (0u),
56       requestTimeout_ (ClockService::milliseconds(DEFAULT_REQUEST_TIMEOUT_MS)),
57       timeout_ ("senf::term::BaseTelnetProtocol::timeout", 0)
58 {
59     SENF_ASSERT( false );
60 }
61
62 prefix_ void senf::term::BaseTelnetProtocol::write(std::string const & s)
63 {
64     for (std::string::const_iterator i (s.begin()); i != s.end(); ++i)
65         write(*i);
66 }
67
68 prefix_ void senf::term::BaseTelnetProtocol::write(char c)
69 {
70     switch (c) {
71     case '\r':
72         transmit('\r');
73         transmit('\0');
74         break;
75     case '\n':
76         transmit('\r');
77         transmit('\n');
78         break;
79     case '\xff':
80         transmit('\xff');
81         transmit('\xff');
82         break;
83     default:
84         transmit(c);
85         break;
86     }
87 }
88
89 prefix_ void
90 senf::term::BaseTelnetProtocol::sendOptionParameters(option_type option,
91                                                                 std::string const & data)
92 {
93     transmit(CMD_IAC);
94     transmit(CMD_SB);
95     transmit(option);
96     for (std::string::const_iterator i (data.begin()); i != data.end(); ++i)
97         if (*i == '\xff') {
98             transmit('\xff');
99             transmit('\xff');
100         }
101         else
102             transmit(*i);
103     transmit(CMD_IAC);
104     transmit(CMD_SE);
105 }
106
107 prefix_ void senf::term::BaseTelnetProtocol::v_handleNOP()
108 {}
109
110 prefix_ void senf::term::BaseTelnetProtocol::v_handleBRK()
111 {}
112
113 prefix_ void senf::term::BaseTelnetProtocol::v_handleIP()
114 {}
115
116 prefix_ void senf::term::BaseTelnetProtocol::v_handleAO()
117 {}
118
119 prefix_ void senf::term::BaseTelnetProtocol::v_handleAYT()
120 {}
121
122 prefix_ void senf::term::BaseTelnetProtocol::v_handleEC()
123 {}
124
125 prefix_ void senf::term::BaseTelnetProtocol::v_handleEL()
126 {}
127
128 prefix_ void senf::term::BaseTelnetProtocol::v_handleGA()
129 {}
130
131 prefix_ void senf::term::BaseTelnetProtocol::handleChar(char c)
132 {
133     switch (charState_) {
134     case NORMAL:
135         handleNormalChar(c);
136         break;
137     case IAC_SEEN:
138         handleCommand(static_cast<unsigned char>(c));
139         break;
140     case EXPECT_OPTION:
141         handleOption(c);
142         break;
143     case CR_SEEN:
144         handleCR(c);
145         break;
146     case SB_OPTION:
147         handleSBOption(c);
148         break;
149     case SB_DATA:
150         handleSBData(c);
151         break;
152     case SB_IAC_SEEN:
153         handleSBIAC(c);
154         break;
155     }
156 }
157
158 prefix_ void senf::term::BaseTelnetProtocol::handleNormalChar(char c)
159 {
160     switch (c) {
161     case '\r':
162         charState_ = CR_SEEN;
163         break;
164     case '\xff':
165         charState_ = IAC_SEEN;
166         break;
167     default:
168         emit(c);
169         break;
170     }
171 }
172
173 prefix_ void senf::term::BaseTelnetProtocol::handleCommand(char c)
174 {
175     switch (c) {
176     case CMD_SE:
177         // Ignore spurious SE commands .. they should only occur while in subnegotiation mode
178         charState_ = NORMAL;
179         break;
180     case CMD_NOP:
181     case CMD_DM:
182     case CMD_BRK:
183     case CMD_IP:
184     case CMD_AO:
185     case CMD_AYT:
186     case CMD_EC:
187     case CMD_EL:
188     case CMD_GA:
189         command_ = Command(static_cast<unsigned char>(c));
190         processCommand();
191         charState_ = NORMAL;
192         break;
193     case CMD_SB:
194         command_ = CMD_SB;
195         charState_ = SB_OPTION;
196         break;
197     case CMD_WILL:
198     case CMD_WONT:
199     case CMD_DO:
200     case CMD_DONT:
201         command_ = Command(static_cast<unsigned char>(c));
202         charState_ = EXPECT_OPTION;
203         break;
204     case CMD_IAC:
205         charState_ = NORMAL;
206         emit(CMD_IAC);
207         break;
208     default:
209         emit(CMD_IAC);
210         charState_ = NORMAL;
211         handleChar(c);
212         break;
213     }
214 }        
215
216 prefix_ void senf::term::BaseTelnetProtocol::handleOption(char c)
217 {
218     option_ = c;
219     processCommand();
220     charState_ = NORMAL;
221 }
222
223 prefix_ void senf::term::BaseTelnetProtocol::handleCR(char c)
224 {
225     switch (c) {
226     case '\0':
227         emit('\r');
228         charState_ = NORMAL;
229         break;
230     case '\n':
231         emit('\n');
232         charState_ = NORMAL;
233         break;
234     default:
235         emit('\r');
236         charState_ = NORMAL;
237         handleChar(c);
238         break;
239     }
240 }
241
242 prefix_ void senf::term::BaseTelnetProtocol::handleSBOption(char c)
243 {
244     option_ = c;
245     charState_ = SB_DATA;
246     data_.clear();
247 }
248
249 prefix_ void senf::term::BaseTelnetProtocol::handleSBData(char c)
250 {
251     if (c == '\xff')
252         charState_ = SB_IAC_SEEN;
253     else
254         data_.push_back(c);
255 }
256
257 prefix_ void senf::term::BaseTelnetProtocol::handleSBIAC(char c)
258 {
259     switch (c) {
260     case CMD_IAC:
261         data_.push_back(c);
262         charState_ = SB_DATA;
263         break;
264     case CMD_SE:
265         processCommand();
266         charState_ = NORMAL;
267         break;
268     default:
269         charState_ = IAC_SEEN;
270         handleChar(c);
271         break;
272     }
273 }
274
275 prefix_ void senf::term::BaseTelnetProtocol::processCommand()
276 {
277     switch (command_) {
278     case CMD_NONE:
279     case CMD_SE:
280     case CMD_DM:
281     case CMD_IAC:
282         break;
283     case CMD_NOP:
284         v_handleNOP();
285         break;
286     case CMD_BRK:
287         v_handleBRK();
288         break;
289     case CMD_IP:
290         v_handleIP();
291         break;
292     case CMD_AO:
293         v_handleAO();
294         break;
295     case CMD_AYT:
296         v_handleAYT();
297         break;
298     case CMD_EC:
299         v_handleEC();
300         break;
301     case CMD_EL:
302         v_handleEL();
303         break;
304     case CMD_GA:
305         v_handleGA();
306         break;
307     case CMD_SB:
308     {
309         OptionHandlerMap::const_iterator i (handlers_.find(option_));
310         if (i != handlers_.end())
311             i->second->v_handleOptionParameters(data_);
312         break;
313     }
314     case CMD_WILL:
315     case CMD_WONT:
316         response(getOption(false, option_), command_ == CMD_WILL);
317         break;
318     case CMD_DO:
319     case CMD_DONT:
320         response(getOption(true, option_),  command_ == CMD_DO);
321         break;
322     }
323 }
324
325 prefix_ void senf::term::BaseTelnetProtocol::transmit(char c)
326 {
327     sendQueue_.push_back(c);
328     outputEvent_.enable();
329 }
330
331 prefix_ void senf::term::BaseTelnetProtocol::readHandler(int state)
332 {
333     if (state != senf::scheduler::FdEvent::EV_READ || handle_.eof()) {
334         inputEvent_.disable();
335         v_eof();
336         return;
337     }
338     std::string data;
339     handle_.read(data, 0u);
340     for (std::string::const_iterator i (data.begin()); i != data.end(); ++i)
341         handleChar(*i);
342 }
343
344 prefix_ void senf::term::BaseTelnetProtocol::writeHandler(int state)
345
346     if (state != senf::scheduler::FdEvent::EV_WRITE) {
347         outputEvent_.disable();
348         inputEvent_.disable();
349         v_eof();
350         return;
351     }
352     sendQueue_.erase(sendQueue_.begin(), 
353                      handle_.write(boost::make_iterator_range(
354                                        sendQueue_.begin(), sendQueue_.end())));
355     if (sendQueue_.empty())
356         outputEvent_.disable();
357 }
358
359 prefix_ void senf::term::BaseTelnetProtocol::timeout()
360 {
361     if (pendingRequests_ > 0u) {
362         pendingRequests_ = 0u;
363         v_setupComplete();
364     }
365 }
366
367 prefix_ senf::term::BaseTelnetProtocol::OptInfo &
368 senf::term::BaseTelnetProtocol::getOption(bool local, option_type option)
369 {
370     OptionsMap::iterator i (options_.find(std::make_pair(local, option)));
371     if (i == options_.end())
372         i = options_.insert(std::make_pair(std::make_pair(local, option),
373                                            OptInfo(local, option))).first;
374     return i->second;
375 }
376
377 prefix_ void senf::term::BaseTelnetProtocol::request(OptInfo & info, bool enabled)
378 {
379     info.wantState = enabled ? OptInfo::WANTED : OptInfo::DISABLED;
380     if (enabled != info.enabled) {
381         transmit(CMD_IAC);
382         transmit((info.local ? CMD_WILL : CMD_DO) + (enabled ? 0 : 1));
383         transmit(info.option);
384         info.optionState = OptInfo::REQUEST_SENT;
385         incrementRequestCounter();
386     }
387 }
388
389 prefix_ void senf::term::BaseTelnetProtocol::response(OptInfo & info, bool enabled)
390 {
391     bool decrementCount (false);
392
393     // If this is a response, we need to unconditionally accept it.  If this is a remote
394     // configuration request, we accept it if wantState is wither WANTED or ACCEPTED.  If this is a
395     // response, we never send out a reply. If it is a remote request we send out a reply only if
396     // either a) we reject the request or b) we accept it AND we have changed our own mode.
397     if (info.optionState == OptInfo::REQUEST_SENT) {
398         // This is a response
399         info.optionState = OptInfo::ACKNOWLEDGED;
400         info.enabled = enabled;
401         decrementCount = true;
402     }
403     else if (enabled != info.enabled) {
404         // Request to change the current state
405         if (!enabled || 
406             (enabled && (info.wantState == OptInfo::WANTED || info.wantState == OptInfo::ACCEPTED))) {
407             // accept the request
408             info.optionState = OptInfo::ACKNOWLEDGED;
409             info.enabled = enabled;
410         }   // else reject the request
411         transmit(CMD_IAC);
412         transmit((info.local ? CMD_WILL : CMD_DO) + (info.enabled ? 0 : 1));
413         transmit(info.option);
414     }
415     else
416         return;
417     if (info.enabled) {
418         OptionHandlerMap::const_iterator i (handlers_.find(info.option));
419         if (i != handlers_.end())
420             i->second->v_init();
421     }
422     if (decrementCount)
423         // This call must be AFTER calling v_init since v_init might increment the request count
424         // and v_setupComplete() might be called prematurely.
425         decrementRequestCounter();
426 }
427
428 prefix_ void senf::term::BaseTelnetProtocol::decrementRequestCounter()
429 {
430     if (pendingRequests_ > 0u) {
431         -- pendingRequests_;
432         if (pendingRequests_ == 0u) {
433             timeout_.disable();
434             v_setupComplete();
435         }
436     }
437 }
438
439 ///////////////////////////////////////////////////////////////////////////
440 // senf::term::telnethandler::TerminalType
441
442 prefix_ senf::term::telnethandler::TerminalType::TerminalType()
443 {
444     registerHandler(this);
445 }
446
447 prefix_ void senf::term::telnethandler::TerminalType::nextTerminalType()
448 {
449     sendOptionParameters(telnetopt::TERMINAL_TYPE, "\x01");
450 }
451
452 prefix_ void senf::term::telnethandler::TerminalType::
453 v_handleOptionParameters(std::string const & data)
454 {
455     if (data.size() <= 0)
456         return;
457     if (data[0] == '\x00') {
458         type_ = data.substr(1);
459         decrementRequestCounter();
460     }
461 }
462
463 prefix_ void senf::term::telnethandler::TerminalType::v_init()
464 {
465     nextTerminalType();
466     incrementRequestCounter();
467 }
468
469 ///////////////////////////////////////////////////////////////////////////
470 // senf::term::telnethandler::NAWS
471
472 prefix_ senf::term::telnethandler::NAWS::NAWS()
473     : width_ (0u), height_ (0u)
474 {
475     registerHandler(this);
476 }
477
478 prefix_ void senf::term::telnethandler::NAWS::v_init()
479 {
480     incrementRequestCounter();
481 }
482
483 prefix_ void
484 senf::term::telnethandler::NAWS::v_handleOptionParameters(std::string const & data)
485 {
486     if (data.size() != 4)
487         return;
488     width_ = (static_cast<unsigned char>(data[0])<<8)+static_cast<unsigned char>(data[1]);
489     height_ = (static_cast<unsigned char>(data[2])<<8)+static_cast<unsigned char>(data[3]);
490     if (! requestsPending())
491         v_windowSizeChanged();
492     decrementRequestCounter();
493 }
494
495 ///////////////////////////////cc.e////////////////////////////////////////
496 #undef prefix_
497 //#include "Telnet.mpp"
498
499 \f
500 // Local Variables:
501 // mode: c++
502 // fill-column: 100
503 // comment-column: 40
504 // c-file-style: "senf"
505 // indent-tabs-mode: nil
506 // ispell-local-dictionary: "american"
507 // compile-command: "scons -u test"
508 // End: