|
1 // vtc_tcp.cpp |
|
2 // |
|
3 // Copyright (c) 2007 - 2010 Accenture. All rights reserved. |
|
4 // This component and the accompanying materials are made available |
|
5 // under the terms of the "Eclipse Public License v1.0" |
|
6 // which accompanies this distribution, and is available |
|
7 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 // |
|
9 // Initial Contributors: |
|
10 // Accenture - Initial contribution |
|
11 // |
|
12 |
|
13 #include <e32std.h> |
|
14 #include <e32cons.h> |
|
15 #include <c32comm.h> |
|
16 #include <e32math.h> |
|
17 #include <es_sock.h> |
|
18 #include <in_sock.h> |
|
19 #include <commdbconnpref.h> |
|
20 #include <fshell/consoleextensions.h> |
|
21 #include <fshell/vtc_base.h> |
|
22 #include <fshell/vtc_controller.h> |
|
23 |
|
24 const TInt KUninitialized = -1; |
|
25 |
|
26 class TPortConfig |
|
27 { |
|
28 public: |
|
29 TPortConfig(); |
|
30 public: |
|
31 TName iHost; |
|
32 TInt iPort; |
|
33 TInt iIapId; |
|
34 TInt iNetworkId; |
|
35 TInt iProtocolId; |
|
36 TInt iImplicit; |
|
37 }; |
|
38 |
|
39 TPortConfig::TPortConfig() |
|
40 : iPort(8080), iIapId(KUninitialized), iNetworkId(KUninitialized), iProtocolId(KProtocolInetTcp), iImplicit(0) |
|
41 { |
|
42 } |
|
43 |
|
44 NONSHARABLE_CLASS(CVtcTcpConsole) : public CVtcConsoleBase |
|
45 { |
|
46 public: |
|
47 CVtcTcpConsole(); |
|
48 ~CVtcTcpConsole(); |
|
49 private: // From CVtcSerialConsole. |
|
50 virtual void ConstructL(const TDesC& aTitle); |
|
51 private: // From MConsoleOutput. |
|
52 virtual TInt Output(const TDesC8& aDes); |
|
53 private: // From MConsoleInput. |
|
54 virtual void Input(TDes8& aDes, TRequestStatus& aStatus); |
|
55 virtual void CancelInput(TRequestStatus& aStatus); |
|
56 private: |
|
57 TInt ConfigurePort(const TDesC& aConfig); |
|
58 TInt ReadConfig(const TDesC& aConfigDes, TPortConfig& aConfig); |
|
59 TInt Accept(const TPortConfig& aConfig); |
|
60 TInt Connect(const TPortConfig& aConfig); |
|
61 |
|
62 TInt Query(TRequestStatus& stat, TRefByValue<const TDesC> aFmt, ...); |
|
63 void CancelQuery(); |
|
64 void CleanupQuery(); |
|
65 |
|
66 private: |
|
67 RSocketServ iSocketServ; |
|
68 RConnection iConnection; |
|
69 RSocket iListeningSocket; |
|
70 RSocket iClientSocket; |
|
71 TSockXfrLength iSockXfrLength; |
|
72 RNotifier iNotifier; |
|
73 TInt iQueryResult; |
|
74 }; |
|
75 |
|
76 |
|
77 CVtcTcpConsole::CVtcTcpConsole() |
|
78 { |
|
79 } |
|
80 |
|
81 CVtcTcpConsole::~CVtcTcpConsole() |
|
82 { |
|
83 iNotifier.Close(); |
|
84 |
|
85 iListeningSocket.Close(); |
|
86 iClientSocket.Close(); |
|
87 iConnection.Close(); |
|
88 iSocketServ.Close(); |
|
89 } |
|
90 |
|
91 class TOverflowTruncate : public TDes16Overflow |
|
92 { |
|
93 public: |
|
94 virtual void Overflow(TDes16&) {} |
|
95 }; |
|
96 |
|
97 TInt CVtcTcpConsole::Query(TRequestStatus& stat, TRefByValue<const TDesC> aFmt, ...) |
|
98 { |
|
99 TOverflowTruncate overflow; |
|
100 VA_LIST list; |
|
101 VA_START(list, aFmt); |
|
102 TBuf<0x100> buf; |
|
103 buf.AppendFormatList(aFmt, list, &overflow); |
|
104 |
|
105 if (iUnderlyingConsole) |
|
106 { |
|
107 Message(EInformation, buf); |
|
108 // when using a console, the accept can be cancelled by hitting ctrl-c |
|
109 return KErrNotSupported; |
|
110 } |
|
111 else |
|
112 { |
|
113 TInt err = KErrNone; |
|
114 if (iNotifier.Handle() == KNullHandle) |
|
115 { |
|
116 err = iNotifier.Connect(); |
|
117 } |
|
118 if (err == KErrNone) |
|
119 { |
|
120 iNotifier.Notify(_L("vt100tcpcons"), buf, _L("OK"), _L("Cancel"), iQueryResult, stat); |
|
121 } |
|
122 return err; |
|
123 } |
|
124 } |
|
125 |
|
126 void CVtcTcpConsole::CancelQuery() |
|
127 { |
|
128 if (iNotifier.Handle()) |
|
129 { |
|
130 iNotifier.NotifyCancel(); // Annoyingly this doesn't appear to be implemented, so we can't auto-dismiss the dialog. |
|
131 iNotifier.Close(); |
|
132 } |
|
133 } |
|
134 |
|
135 void CVtcTcpConsole::ConstructL(const TDesC& aTitle) |
|
136 { |
|
137 User::LeaveIfError(ConfigurePort(aTitle)); |
|
138 CVtcConsoleBase::ConstructL(aTitle); |
|
139 } |
|
140 |
|
141 TInt CVtcTcpConsole::ReadConfig(const TDesC& aConfigDes, TPortConfig& aConfig) |
|
142 { |
|
143 _LIT(KKeywordHost, "host"); |
|
144 _LIT(KKeywordPort, "port"); |
|
145 _LIT(KKeywordIapId, "iap"); |
|
146 _LIT(KKeywordNetworkId, "network"); |
|
147 _LIT(KKeywordProtocolId, "protocol"); |
|
148 _LIT(KKeywordDebug, "debug"); |
|
149 _LIT(KKeywordImplicit, "implicit"); |
|
150 |
|
151 TInt err = KErrNone; |
|
152 TLex lex(aConfigDes); |
|
153 while (!lex.Eos()) |
|
154 { |
|
155 TPtrC keyword; |
|
156 TPtrC valueDes; |
|
157 err = ReadKeywordValuePair(lex, keyword, valueDes); |
|
158 if (err != KErrNone) |
|
159 { |
|
160 break; |
|
161 } |
|
162 |
|
163 if (keyword == KKeywordHost) |
|
164 { |
|
165 aConfig.iHost = valueDes; |
|
166 } |
|
167 else |
|
168 { |
|
169 TInt valueInt; |
|
170 TLex lex(valueDes); |
|
171 err = lex.Val(valueInt); |
|
172 |
|
173 if (keyword == KKeywordPort) |
|
174 { |
|
175 aConfig.iPort = valueInt; |
|
176 } |
|
177 else if (keyword == KKeywordIapId) |
|
178 { |
|
179 aConfig.iIapId = valueInt; |
|
180 } |
|
181 else if (keyword == KKeywordNetworkId) |
|
182 { |
|
183 aConfig.iNetworkId = valueInt; |
|
184 } |
|
185 else if (keyword == KKeywordProtocolId) |
|
186 { |
|
187 aConfig.iProtocolId = valueInt; |
|
188 } |
|
189 else if (keyword == KKeywordDebug) |
|
190 { |
|
191 SetDebug(valueInt); |
|
192 } |
|
193 else if (keyword == KKeywordImplicit) |
|
194 { |
|
195 aConfig.iImplicit = valueInt; |
|
196 } |
|
197 } |
|
198 } |
|
199 |
|
200 return err; |
|
201 } |
|
202 |
|
203 TInt CVtcTcpConsole::ConfigurePort(const TDesC& aConfig) |
|
204 { |
|
205 TPortConfig portConfig; |
|
206 TInt err = ReadConfig(aConfig, portConfig); |
|
207 if (err == KErrNone) |
|
208 { |
|
209 err = iSocketServ.Connect(); |
|
210 } |
|
211 if ((err == KErrNone) && !portConfig.iImplicit) |
|
212 { |
|
213 err = iConnection.Open(iSocketServ); |
|
214 if (err == KErrNone) |
|
215 { |
|
216 Message(EDebug, _L("Starting connection...")); |
|
217 TCommDbConnPref prefs; |
|
218 if (portConfig.iIapId == KUninitialized) |
|
219 { |
|
220 prefs.SetDialogPreference(ECommDbDialogPrefPrompt); |
|
221 } |
|
222 else |
|
223 { |
|
224 prefs.SetIapId(portConfig.iIapId); |
|
225 prefs.SetDialogPreference(ECommDbDialogPrefDoNotPrompt); |
|
226 } |
|
227 err = iConnection.Start(prefs); |
|
228 if (err) |
|
229 { |
|
230 Message(EError, _L("Connection failed (%d)"), err); |
|
231 } |
|
232 } |
|
233 } |
|
234 if (err == KErrNone) |
|
235 { |
|
236 if (portConfig.iHost.Length() > 0) |
|
237 { |
|
238 err = Connect(portConfig); |
|
239 } |
|
240 else |
|
241 { |
|
242 err = Accept(portConfig); |
|
243 } |
|
244 } |
|
245 |
|
246 if (err == KErrNone) |
|
247 { |
|
248 iClientSocket.SetOpt(KSoTcpNoDelay, KSolInetTcp, ETrue); // Ignore error (may not be supported). |
|
249 Message(EDebug, _L("Connected.")); |
|
250 } |
|
251 |
|
252 if (err != KErrNone) |
|
253 { |
|
254 iClientSocket.Close(); |
|
255 iListeningSocket.Close(); |
|
256 iConnection.Close(); |
|
257 iSocketServ.Close(); |
|
258 } |
|
259 |
|
260 return err; |
|
261 } |
|
262 |
|
263 TInt CVtcTcpConsole::Connect(const TPortConfig& aConfig) |
|
264 { |
|
265 Message(EDebug, _L("Connecting to %S:%u"), &aConfig.iHost, aConfig.iPort); |
|
266 TInetAddr addr(aConfig.iPort); |
|
267 TInt err = addr.Input(aConfig.iHost); |
|
268 if (err) |
|
269 { |
|
270 RHostResolver resolver; |
|
271 if (aConfig.iImplicit) |
|
272 { |
|
273 err = (resolver.Open(iSocketServ, KAfInet, aConfig.iProtocolId)); |
|
274 } |
|
275 else |
|
276 { |
|
277 err = (resolver.Open(iSocketServ, KAfInet, aConfig.iProtocolId, iConnection)); |
|
278 } |
|
279 if (err == KErrNone) |
|
280 { |
|
281 TNameEntry nameEntry; |
|
282 err = resolver.GetByName(aConfig.iHost, nameEntry); |
|
283 if (err == KErrNone) |
|
284 { |
|
285 addr.SetAddress(TInetAddr::Cast(nameEntry().iAddr).Address()); |
|
286 } |
|
287 resolver.Close(); |
|
288 } |
|
289 } |
|
290 if (err == KErrNone) |
|
291 { |
|
292 if (aConfig.iImplicit) |
|
293 { |
|
294 err = iClientSocket.Open(iSocketServ, KAfInet, KSockStream, aConfig.iProtocolId); |
|
295 } |
|
296 else |
|
297 { |
|
298 err = iClientSocket.Open(iSocketServ, KAfInet, KSockStream, aConfig.iProtocolId, iConnection); |
|
299 } |
|
300 } |
|
301 if (err == KErrNone) |
|
302 { |
|
303 TRequestStatus status; |
|
304 iClientSocket.Connect(addr, status); |
|
305 User::WaitForRequest(status); |
|
306 err = status.Int(); |
|
307 } |
|
308 if (err != KErrNone) |
|
309 { |
|
310 Message(EError, _L("Connection to %S:%u failed (%d)"), &aConfig.iHost, aConfig.iPort, err); |
|
311 } |
|
312 return err; |
|
313 } |
|
314 |
|
315 TInt CVtcTcpConsole::Accept(const TPortConfig& aConfig) |
|
316 { |
|
317 TInt err = KErrNone; |
|
318 Message(EDebug, _L("Opening listening socket...")); |
|
319 if (aConfig.iImplicit) |
|
320 { |
|
321 err = iListeningSocket.Open(iSocketServ, KAfInet, KSockStream, aConfig.iProtocolId); |
|
322 } |
|
323 else |
|
324 { |
|
325 err = iListeningSocket.Open(iSocketServ, KAfInet, KSockStream, aConfig.iProtocolId, iConnection); |
|
326 } |
|
327 if (err == KErrNone) |
|
328 { |
|
329 TInetAddr addr(aConfig.iPort); |
|
330 Message(EDebug, _L("Binding listening socket...")); |
|
331 err = iListeningSocket.Bind(addr); |
|
332 if (err) |
|
333 { |
|
334 Message(EError, _L("Bind failed (%d)"), err); |
|
335 } |
|
336 } |
|
337 if (err == KErrNone) |
|
338 { |
|
339 err = iListeningSocket.Listen(1); |
|
340 } |
|
341 if (err == KErrNone) |
|
342 { |
|
343 err = iClientSocket.Open(iSocketServ); |
|
344 } |
|
345 if (err == KErrNone) |
|
346 { |
|
347 // Find our local IP address. |
|
348 TInetAddr addr; |
|
349 addr.SetAddress(KInetAddrAny); |
|
350 TPckgBuf<TSoInetIfQuery> query; |
|
351 query().iDstAddr = addr; |
|
352 TInt err2 = iListeningSocket.GetOpt(KSoInetIfQueryByDstAddr, KSolInetIfQuery, query); |
|
353 TInt qerr; |
|
354 |
|
355 TRequestStatus queryStatus; |
|
356 if (err2 == KErrNone) |
|
357 { |
|
358 TBuf<128> addrName; |
|
359 query().iSrcAddr.Output(addrName); |
|
360 qerr = Query(queryStatus, _L("Listening on %S:%d..."), &addrName, aConfig.iPort); |
|
361 } |
|
362 else |
|
363 { |
|
364 qerr = Query(queryStatus, _L("Listening on port %d..."), aConfig.iPort); |
|
365 } |
|
366 |
|
367 // Start the accept. |
|
368 TRequestStatus acceptStatus; |
|
369 iListeningSocket.Accept(iClientSocket, acceptStatus); |
|
370 |
|
371 if (qerr == KErrNone) |
|
372 { |
|
373 User::WaitForRequest(acceptStatus, queryStatus); |
|
374 if (acceptStatus != KRequestPending) |
|
375 { |
|
376 CancelQuery(); |
|
377 User::WaitForRequest(queryStatus); |
|
378 } |
|
379 else |
|
380 { |
|
381 if (iQueryResult == 1) |
|
382 { |
|
383 if (acceptStatus == KRequestPending) |
|
384 { |
|
385 iListeningSocket.CancelAccept(); |
|
386 } |
|
387 User::WaitForRequest(acceptStatus); |
|
388 } |
|
389 else |
|
390 { |
|
391 User::WaitForRequest(acceptStatus); |
|
392 } |
|
393 CancelQuery(); |
|
394 } |
|
395 } |
|
396 else |
|
397 { |
|
398 User::WaitForRequest(acceptStatus); |
|
399 } |
|
400 |
|
401 err = acceptStatus.Int(); |
|
402 if (err) |
|
403 { |
|
404 Message(EError, _L("Accept failed (%d)"), err); |
|
405 } |
|
406 else |
|
407 { |
|
408 TInetAddr remote; |
|
409 iClientSocket.RemoteName(remote); |
|
410 TBuf<128> addrName; |
|
411 remote.Output(addrName); |
|
412 Message(EInformation, _L("Remote %S connected."), &addrName); |
|
413 } |
|
414 } |
|
415 return err; |
|
416 } |
|
417 |
|
418 TInt CVtcTcpConsole::Output(const TDesC8& aDes) |
|
419 { |
|
420 TRequestStatus status; |
|
421 iClientSocket.Write(aDes, status); |
|
422 User::WaitForRequest(status); |
|
423 return status.Int(); |
|
424 } |
|
425 |
|
426 void CVtcTcpConsole::Input(TDes8& aDes, TRequestStatus& aStatus) |
|
427 { |
|
428 iClientSocket.RecvOneOrMore(aDes, 0, aStatus, iSockXfrLength); |
|
429 } |
|
430 |
|
431 void CVtcTcpConsole::CancelInput(TRequestStatus&) |
|
432 { |
|
433 iClientSocket.CancelRead(); |
|
434 } |
|
435 |
|
436 EXPORT_C TAny* NewConsole() |
|
437 { |
|
438 return new CVtcTcpConsole; |
|
439 } |
|
440 |