plptools
Loading...
Searching...
No Matches
ncp.cc
Go to the documentation of this file.
1/*
2 * This file is part of plptools.
3 *
4 * Copyright (C) 1999 Philip Proudman <philip.proudman@btinternet.com>
5 * Copyright (C) 1999-2001 Fritz Elfert <felfert@to.com>
6 * Copyright (C) 2026 Jason Morley <hello@jbmorley.co.uk>
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 along
19 * along with this program; if not, see <https://www.gnu.org/licenses/>.
20 *
21 */
22#include "config.h"
23
24#include <iostream>
25#include <string>
26
27#include <time.h>
28
29#include "bufferstore.h"
30#include "bufferarray.h"
31
32#include "link.h"
33#include "linkchannel.h"
34#include "ncp_log.h"
35#include "ncp.h"
36#include "ncpstatuscallback.h"
37#include "rfsv.h"
38
39using namespace std;
40
41NCP::NCP(const char *fname,
42 int baud,
43 bool noDSRCheck,
44 unsigned short _verbose,
45 const int cancellationFd,
46 NCPStatusCallback statusCallback,
47 void *callbackContext)
48: verbose(_verbose)
49, statusCallback_(statusCallback)
50, callbackContext_(callbackContext) {
51
55
56 // until detected on receipt of INFO we use these.
59 lChan = NULL;
60
61 // init channels
62 for (int i = 0; i < MAX_CHANNELS_PSION; i++)
63 channelPtr[i] = NULL;
64
65 l = new Link(fname, baud, this, noDSRCheck, verbose, cancellationFd);
66}
67
70 for (int i = 0; i < maxLinks(); i++) {
71 if (isValidChannel(i)) {
72 BufferStore b2;
75 }
76 channelPtr[i] = NULL;
77 }
79 delete l;
80 delete [] channelPtr;
81 delete [] remoteChanList;
82 delete [] messageList;
83}
84
86 return maxChannels;
87}
88
89void NCP::reset() {
90 for (int i = 0; i < maxLinks(); i++) {
91 if (isValidChannel(i))
92 channelPtr[i]->terminateWhenAsked();
93 channelPtr[i] = NULL;
94 }
95 failed = false;
96 if (lChan)
97 delete(lChan);
98 lChan = NULL;
99 protocolVersion = PV_SERIES_5; // until detected on receipt of INFO
100 l->reset();
101}
102
104{
105 return protocolVersion;
106}
107
109 if (s.getLen() > 1) {
110 int channel = s.getByte(0);
112 if (channel == 0) {
114 } else {
115 int allData = s.getByte(1);
117
119 channel = lastSentChannel;
120 }
121
122 if (!isValidChannel(channel)) {
123 lerr << "ncp: Got message for unknown channel\n";
124 } else {
125 messageList[channel].addBuff(s);
126 if (allData == LAST_MESS) {
127 channelPtr[channel]->ncpDataCallback(messageList[channel]);
128 messageList[channel].init();
129 } else if (allData != NOT_LAST_MESS) {
130 lerr << "ncp: bizarre third byte!\n";
131 }
132 }
133 }
134 } else
135 lerr << "Got null message\n";
136}
137
139 BufferStore open;
140 open.addByte(0); // control
141
142 open.addByte(chan);
143 open.addByte(t);
144 open.addBuff(command);
146 lout << "ncp: >> " << ctrlMsgName(t) << " " << chan << endl;
147 l->send(open);
148}
149
150PcServer *NCP::findPcServer(const char *name) {
151 if (name) {
152 vector<PcServer>::iterator i;
153 for (i = pcServers.begin(); i != pcServers.end(); i++)
154 if (i->getName() == name)
155 return i->self();
156 }
157 return NULL;
158}
159
160void NCP::registerPcServer(TCPSocket *skt, const char *name) {
161 pcServers.push_back(PcServer(skt, name));
162}
163
165 if (server) {
166 vector<PcServer>::iterator i;
167 for (i = pcServers.begin(); i != pcServers.end(); i++)
168 if (i->self() == server) {
169 pcServers.erase(i);
170 return;
171 }
172 }
173}
174
176 int remoteChan = buff.getByte(0);
177
179 buff.discardFirstBytes(2);
181 lout << "ncp: << " << ctrlMsgName(imt) << " " << remoteChan;
182
183 BufferStore b;
184 int localChan;
185
186 switch (imt) {
188 if (verbose & NCP_DEBUG_LOG) {
190 lout << " [" << buff << "]";
191 lout << endl;
192 }
193
194 failed = false;
195 if (!strcmp(buff.getString(0), "LINK.*")) {
196 if (lChan)
197 localChan = lChan->getNcpChannel();
198 else
199 localChan = getFirstUnusedChan();
200
201 // Ack with connect response
202 b.addByte(remoteChan);
203 b.addByte(0);
206 lout << "ncp: Link UP" << endl;
207 if (statusCallback_) {
209 }
210 linf << _("Connected with a S")
211 << ((protocolVersion == PV_SERIES_5) ? 5 : 3) << _(" at ")
212 << getSpeed() << _(" baud") << endl;
213 // Create linkchan if it does not yet exist
214 if (!lChan) {
216 lout << "ncp: new passive LinkChannel" << endl;
217 channelPtr[localChan] =
218 lChan = new LinkChannel(this, localChan);
220 }
222 } else {
223 PcServer *s = findPcServer(buff.getString(0));
224 bool ok = false;
225
226 if (s) {
227 localChan = getFirstUnusedChan();
228 ok = s->clientConnect(localChan, remoteChan);
229 if (!ok)
230 // release channel ptr
231 channelPtr[localChan] = NULL;
232 }
233 b.addByte(remoteChan);
234 if (ok) {
237 lout << "ncp: ACCEPT client connect" << endl;
238 } else {
239 localChan = 0;
242 lout << "ncp: REJECT client connect" << endl;
243 }
245
246 // Create linkchan if it does not yet exist
247 if (!lChan) {
249 lout << "ncp: new active LinkChannel" << endl;
250 channelPtr[localChan] =
251 lChan = new LinkChannel(this, -1);
253 }
254
255 }
256 break;
257
259
260 int forChan;
261
262 failed = false;
263 forChan = buff.getByte(0);
265 lout << " ch=" << forChan << " stat=";
266 if (buff.getByte(1) == 0) {
268 lout << "OK" << endl;
269 if (isValidChannel(forChan)) {
270 remoteChanList[forChan] = remoteChan;
271 channelPtr[forChan]->ncpConnectAck();
272 } else {
274 lout << "ncp: message for unknown channel" << endl;
275 }
276 } else {
278 lout << "Unknown " << (int) buff.getByte(1) << endl;
279 if (isValidChannel(forChan))
280 channelPtr[forChan]->ncpConnectNak();
281 }
282 break;
283
285
286 int ver;
287
288 failed = false;
289 ver = buff.getByte(0);
290 // Series 3c returns '3', as does mclink. PsiWin 1.1
291 // returns version 2. We return whatever version we're
292 // sent, which is rather crude, but works for Series 3
293 // and probably 5. If Symbian have changed EPOC Connect
294 // for the Series 5mx/7, this may need to change.
295 //
296 if (ver == PV_SERIES_5 || ver == PV_SERIES_3) {
297 BufferStore b;
298 protocolVersion = ver;
299 if (verbose & NCP_DEBUG_LOG) {
301 lout << " [" << buff << "]";
302 lout << endl;
303 }
304 // Fake NCP version 2 for a Series 3 (behave like PsiWin 1.1)
305 if (ver == PV_SERIES_3) {
306 ver = 2;
307 } else {
308 // Series 5 supports more channels
310 }
311 b.addByte(ver);
312 // Do we send a time of 0 or a real time?
313 // The Psion uses this to determine whether to
314 // restart. (See protocol docs for details)
315 b.addDWord(time(NULL));
317 } else {
318 lout << "ALERT!!!! Unexpected Protocol Version!! (Not Series 3/5?)!" << endl;
319 failed = true;
320 }
321 break;
322
325 lout << " ch=" << (int) buff.getByte(0) << endl;
326 disconnect(buff.getByte(0));
327 l->purgeQueue(remoteChan);
328 if (statusCallback_) {
330 }
331 break;
332
336 case NCON_MSG_NCP_END:
337 default:
338 if (verbose & NCP_DEBUG_LOG) {
340 lout << " [" << buff << "]";
341 lout << endl;
342 }
343
344 }
345}
346
348 for (int cNum = 1; cNum < maxLinks(); cNum++) {
349 if (channelPtr[cNum] == NULL) {
351 lout << "ncp: getFirstUnusedChan=" << cNum << endl;
352 channelPtr[cNum] = (Channel *)0xdeadbeef;
353 return cNum;
354 }
355 }
356 return 0;
357}
358
359bool NCP::isValidChannel(int channel) {
360 return (channelPtr[channel] && ((long)channelPtr[channel] != 0xdeadbeef));
361}
362
363void NCP::RegisterAck(int chan, const char *name) {
365 lout << "ncp: RegisterAck: chan=" << chan << endl;
366 for (int cNum = 1; cNum < maxLinks(); cNum++) {
367 Channel *ch = channelPtr[cNum];
368 if (isValidChannel(cNum) && ch->getNcpChannel() == chan) {
369 ch->setNcpConnectName(name);
370 ch->ncpRegisterAck();
371 return;
372 }
373 }
374 lerr << "ncp: RegisterAck: no channel to deliver" << endl;
375}
376
378 if (lChan) {
379 int cNum = ch->getNcpChannel();
380 if (cNum == 0)
381 cNum = getFirstUnusedChan();
382 if (cNum > 0) {
383 channelPtr[cNum] = ch;
384 ch->setNcpChannel(cNum);
385 lChan->Register(ch);
386 } else
387 lerr << "ncp: Out of channels in register" << endl;
388 } else
389 lerr << "ncp: Register without established lChan" << endl;
390}
391
393 // look for first unused chan
394
395 int cNum = ch->getNcpChannel();
396 if (cNum == 0)
397 cNum = getFirstUnusedChan();
398 if (cNum > 0) {
399 channelPtr[cNum] = ch;
400 ch->setNcpChannel(cNum);
401 BufferStore b;
402 if (ch->getNcpConnectName())
404 else {
406 b.addString(".*");
407 }
408 b.addByte(0);
410 return cNum;
411 }
412 return -1;
413}
414
415void NCP::send(int channel, BufferStore & a) {
416 bool last;
417 do {
418 last = true;
419
420 if (a.getLen() > NCP_SENDLEN)
421 last = false;
422
423 BufferStore out;
424 out.addByte(remoteChanList[channel]);
425 out.addByte(channel);
426
427 if (last) {
428 out.addByte(LAST_MESS);
429 } else {
431 }
432
433 out.addBuff(a, NCP_SENDLEN);
434 a.discardFirstBytes(NCP_SENDLEN);
435 l->send(out);
436 } while (!last);
437 lastSentChannel = channel;
438}
439
440void NCP::disconnect(int channel) {
441 if (!isValidChannel(channel)) {
442 lerr << "ncp: Ignored disconnect for unknown channel #" << channel << endl;
443 return;
444 }
445 channelPtr[channel]->terminateWhenAsked();
447 lout << "ncp: disconnect: channel=" << channel << endl;
448 channelPtr[channel] = NULL;
449 BufferStore b;
450 b.addByte(remoteChanList[channel]);
452}
453
455 return l->stuffToSend();
456}
457
459 bool lfailed = l->hasFailed();
460 if (failed || lfailed) {
462 lout << "ncp: hasFailed: " << failed << ", " << lfailed << endl;
463 }
464 failed |= lfailed;
465 if (failed) {
466 if (statusCallback_) {
468 }
469 if (lChan) {
470 channelPtr[lChan->getNcpChannel()] = NULL;
471 delete lChan;
472 }
473 lChan = NULL;
474 }
475 return failed;
476}
477
479 return (lChan != NULL);
480}
481
483 return l->getSpeed();
484}
485
486const char *NCP::ctrlMsgName(unsigned char msgType) {
487 switch (msgType) {
489 return "NCON_MSG_DATA_XOFF";
491 return "NCON_MSG_DATA_XON";
493 return "NCON_MSG_CONNECT_TO_SERVER";
495 return "NCON_MSG_CONNECT_RESPONSE";
497 return "NCON_MSG_CHANNEL_CLOSED";
499 return "NCON_MSG_NCP_INFO";
501 return "NCON_MSG_CHANNEL_DISCONNECT";
502 case NCON_MSG_NCP_END:
503 return "NCON_MSG_NCP_END";
504 }
505 return "NCON_MSG_UNKNOWN";
506}
A generic container for an array of bytes.
Definition: bufferstore.h:36
const char * getString(long pos=0) const
Retrieves the characters at index pos.
Definition: bufferstore.cc:118
void addByte(unsigned char c)
Appends a byte to the content of this instance.
Definition: bufferstore.cc:157
void addBuff(const BufferStore &b, long maxLen=-1)
Appends data to the content of this instance.
Definition: bufferstore.cc:180
void addDWord(long dw)
Appends a dword to the content of this instance.
Definition: bufferstore.cc:197
unsigned long getLen() const
Retrieves the length of a BufferStore.
Definition: bufferstore.cc:92
unsigned char getByte(long pos=0) const
Retrieves the byte at index pos.
Definition: bufferstore.cc:96
void addString(const char *s)
Appends a string to the content of this instance.
Definition: bufferstore.cc:162
void discardFirstBytes(int len=0)
Removes bytes from the start of the buffer.
Definition: bufferstore.cc:141
void init()
Initializes the BufferStore.
Definition: bufferstore.cc:74
const char * getNcpConnectName()
Definition: channel.cc:116
void setVerbose(short int _verbose)
Definition: channel.cc:108
virtual void ncpDataCallback(BufferStore &a)=0
virtual void ncpConnectAck()=0
void setNcpConnectName(const char *)
Definition: channel.cc:120
virtual const char * getNcpRegisterName()=0
void setNcpChannel(int chan)
Definition: channel.cc:96
void terminateWhenAsked()
Definition: channel.cc:55
int getNcpChannel(void)
Definition: channel.cc:100
virtual void ncpRegisterAck()=0
void Register(Channel *)
Definition: linkchannel.cc:108
void ncpConnectAck()
Definition: linkchannel.cc:93
@ LAST_MESS
Definition: ncp.h:87
@ NOT_LAST_MESS
Definition: ncp.h:87
const char * ctrlMsgName(unsigned char)
Definition: ncp.cc:486
bool failed
Definition: ncp.h:112
int * remoteChanList
Definition: ncp.h:111
@ PV_SERIES_3
Definition: ncp.h:99
@ PV_SERIES_5
Definition: ncp.h:99
friend class Link
Definition: ncp.h:85
void registerPcServer(TCPSocket *skt, const char *name)
Definition: ncp.cc:160
PcServer * findPcServer(const char *name)
Definition: ncp.cc:150
void unregisterPcServer(PcServer *server)
Definition: ncp.cc:164
BufferStore * messageList
Definition: ncp.h:110
bool isValidChannel(int)
Definition: ncp.cc:359
int maxLinks()
Definition: ncp.cc:85
int lastSentChannel
Definition: ncp.h:117
bool hasFailed()
Definition: ncp.cc:458
NCP(const char *fname, int baud, bool noDSRCheck, unsigned short verbose, const int cancellationFd, NCPStatusCallback statusCallback, void *context)
Definition: ncp.cc:41
interControllerMessageType
Definition: ncp.h:88
@ NCON_MSG_NCP_END
Definition: ncp.h:97
@ NCON_MSG_DATA_XOFF
Definition: ncp.h:90
@ NCON_MSG_NCP_INFO
Definition: ncp.h:95
@ NCON_MSG_CONNECT_TO_SERVER
Definition: ncp.h:92
@ NCON_MSG_CHANNEL_DISCONNECT
Definition: ncp.h:96
@ NCON_MSG_CHANNEL_CLOSED
Definition: ncp.h:94
@ NCON_MSG_CONNECT_RESPONSE
Definition: ncp.h:93
@ NCON_MSG_DATA_XON
Definition: ncp.h:91
LinkChannel * lChan
Definition: ncp.h:114
void decodeControlMessage(BufferStore &buff)
Definition: ncp.cc:175
void Register(Channel *c)
Definition: ncp.cc:377
Link * l
Definition: ncp.h:107
void controlChannel(int chan, enum interControllerMessageType t, BufferStore &command)
Definition: ncp.cc:138
int maxChannels
Definition: ncp.h:115
bool gotLinkChannel()
Definition: ncp.cc:478
void reset()
Definition: ncp.cc:89
Channel ** channelPtr
Definition: ncp.h:109
int getFirstUnusedChan()
Definition: ncp.cc:347
NCPStatusCallback statusCallback_
Definition: ncp.h:118
int getSpeed()
Definition: ncp.cc:482
void send(int channel, BufferStore &a)
Definition: ncp.cc:415
int connect(Channel *c)
Definition: ncp.cc:392
unsigned short verbose
Definition: ncp.h:108
short int getProtocolVersion()
Definition: ncp.cc:103
bool stuffToSend()
Definition: ncp.cc:454
~NCP()
Definition: ncp.cc:68
short int protocolVersion
Definition: ncp.h:113
void disconnect(int channel)
Definition: ncp.cc:440
std::vector< PcServer > pcServers
Definition: ncp.h:116
void * callbackContext_
Definition: ncp.h:119
void receive(BufferStore s)
Definition: ncp.cc:108
void RegisterAck(int, const char *)
Definition: ncp.cc:363
Representation of a server process on the PC A dummy which does not allow connects for now.
Definition: ncp.h:44
PcServer * self()
Definition: ncp.h:50
bool clientConnect(int, int)
Definition: ncp.h:48
@ E_PSI_FILE_NXIST
Definition: rfsv.h:139
@ E_PSI_GEN_NONE
Definition: rfsv.h:114
A class for dealing with sockets.
Definition: tcpsocket.h:37
Definition: doctest.h:522
#define MAX_CHANNELS_SIBO
Definition: ncp.h:34
#define NCP_SENDLEN
Definition: ncp.h:35
#define MAX_CHANNELS_PSION
Definition: ncp.h:33
#define NCP_DEBUG_DUMP
Definition: ncp_log.h:29
std::ostream lerr
std::ostream lout
#define NCP_DEBUG_LOG
Definition: ncp_log.h:28
std::ostream linf
void(* NCPStatusCallback)(void *context, bool isConnected, int protocolVersion)
static RFSV * a
Definition: main.cc:55
#define _(String)
Definition: plpintl.h:34
int verbose
Definition: plpprintd.cc:58