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