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