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