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