3 #include "includes.hpp"
4 #include <wx/protocol/protocol.h>
7 // TRANSPORT LAYER OPERATIONS
12 // Attach to the Citadel server
13 // FIX (add check for not allowed to log in)
14 int CitClient::attach(wxString host, wxString port) {
18 if (sock->IsConnected())
24 sock->Connect(addr, TRUE);
25 if (sock->IsConnected()) {
26 serv_gets(ServerReady);
28 curr_host = host; // Remember host and port, in case
29 curr_port = port; // we need to auto-reconnect later
38 CitClient::CitClient(void) {
40 //wxSocketHandler::Master();
41 sock = new wxSocketClient();
43 // The WAITALL flag causes reads to block. Don't use it.
44 // sock->SetFlags(wxSocketBase::WAITALL);
46 // Guilhem Lavaux suggested using the SPEED flag to keep from
47 // blocking, but it just freezes the app in mid-transaction.
48 sock->SetFlags(wxSocketBase::SPEED);
50 //wxSocketHandler::Master().Register(sock);
51 // sock->SetNotify(wxSocketBase::REQ_LOST);
53 (void)new keepalive(this);
58 CitClient::~CitClient(void) {
59 // Be nice and log out from the server if it's still connected
65 void CitClient::detach(void) {
68 if (sock->IsConnected()) {
77 // Is this client connected? Simply return the IsConnected status of sock.
78 bool CitClient::IsConnected(void) {
79 return sock->IsConnected();
86 // Read a line of text from the server
87 void CitClient::serv_gets(wxString& buf) {
88 static char charbuf[512];
89 static size_t nbytes = 0;
94 for (i=nbytes; i>=0; --i)
95 if (charbuf[i] == 10) nl_pos = i;
97 sock->Read(&charbuf[nbytes], (sizeof(charbuf)-nbytes) );
98 nbytes += sock->LastCount();
100 for (i=nbytes; i>=0; --i)
101 if (charbuf[i] == 10) nl_pos = i;
102 } while (nl_pos < 0);
104 //if (nl_pos != nbytes)
105 // sock->Unread(&charbuf[nl_pos], nbytes-nl_pos);
111 strcpy(charbuf, &charbuf[nl_pos + 1]);
112 nbytes = nbytes - (nl_pos + 1);
125 // Write a line of text to the server
126 void CitClient::serv_puts(wxString buf) {
128 sock->Write((const char *)buf, buf.Len());
129 sock->Write("\n", 1);
141 // SESSION LAYER OPERATIONS
145 // Server transaction (returns first digit of server response code)
146 int CitClient::serv_trans(
150 wxString desired_room
153 int first_digit, i, pos;
154 wxString buf, pw, junk;
155 bool express_messages_waiting = FALSE;
157 // If the caller specified that this transaction must take place
158 // in a particular room, make sure we're in that room.
159 if (desired_room.Length() > 0) {
160 if (desired_room.CmpNoCase(CurrentRoom) != 0) {
162 GotoRoom(desired_room, pw, junk);
167 // If a mutex is to be wrapped around this function in the future,
168 // it must begin HERE.
170 // wxBeginBusyCursor();
174 if (IsConnected() == FALSE) {
175 wxSleep(20); // Give a crashed server some time to restart
182 first_digit = (response.GetChar(0)) - '0';
184 if (response.GetChar(3) == '*')
185 express_messages_waiting = TRUE;
187 if (first_digit == 1) { // LISTING_FOLLOWS
189 while (serv_gets(buf), buf != "000") {
190 xferbuf.Append(buf + "\n");
192 } else if (first_digit == 4) { // SEND_LISTING
194 while (buf.Length() > 0) {
195 pos = buf.Find('\n', FALSE);
196 if ((pos < 0) && (buf.Length() < 250)) {
197 serv_puts(buf + "\n");
199 } else if ((pos < 250) && (pos >= 0)) {
200 serv_puts(buf.Left(pos+1));
201 buf = buf.Mid(pos+1);
203 pos = buf.Left(250).Find(' ', TRUE);
204 if ((pos < 250) && (pos >= 0)) {
205 serv_puts(buf.Left(pos) + "\n");
206 buf = buf.Mid(pos+1);
208 serv_puts(buf.Left(250) + "\n");
216 // If a mutex is to be wrapped around this function in the future,
218 // wxEndBusyCursor();
221 if (express_messages_waiting) {
222 download_express_messages();
228 // Shorter forms of serv_trans()
229 int CitClient::serv_trans(
234 return serv_trans(command, response, xferbuf, "");
237 int CitClient::serv_trans(wxString& command, wxString& response) {
239 return serv_trans(command, response, junklist);
242 int CitClient::serv_trans(wxString& command) {
244 return serv_trans(command, junkbuf);
248 // PRESENTATION LAYER OPERATIONS
252 void CitClient::download_express_messages(void) {
253 wxString sendcmd, recvcmd, x_user, x_sys;
257 while (serv_trans(sendcmd, recvcmd, xferbuf) == 1) {
258 extract(x_user, recvcmd, 3);
259 extract(x_sys, recvcmd, 4);
260 (void)new express_message(this, x_user, x_sys, xferbuf);
266 // Set up some things that we do at the beginning of every session
267 void CitClient::initialize_session(void) {
278 sendcmd = "IDEN 0|6|001|Daphne (Windows)";
279 #elif defined(__WXGTK__)
280 sendcmd - "IDEN 0|6|001|Daphne (GTK)";
281 #elif defined(__WXMOTIF__)
282 sendcmd = "IDEN 0|6|001|Daphne (Motif)";
284 sendcmd = "IDEN 0|6|001|Daphne (Unknown window system)";
289 if (serv_trans(sendcmd, recvcmd, info)==1) {
291 while (pos = info.Find('\n', FALSE), (pos >= 0) ) {
292 infoline = info.Left(pos);
293 info = info.Mid(pos+1);
296 case 0: SessionID = atoi(infoline);
297 case 1: NodeName = infoline;
298 case 2: HumanNode = infoline;
299 case 3: FQDN = infoline;
300 case 4: ServerSoftware = infoline;
301 case 5: ServerRev = atoi(infoline);
302 case 6: GeoLocation = infoline;
303 case 7: SysAdmin = infoline;
304 case 8: ServerType = atoi(infoline);
305 case 9: MorePrompt = infoline;
306 case 10: UseFloors = ((atoi(infoline)>0)
308 case 11: PagingLevel = atoi(infoline);
320 bool CitClient::GotoRoom(wxString roomname, wxString password,
321 wxString& server_response) {
323 wxString sendcmd, recvcmd;
325 sendcmd = "GOTO " + roomname + "|" + password;
326 retval = serv_trans(sendcmd, recvcmd);
327 server_response = recvcmd;
329 if (retval != 2) return FALSE;
331 extract(CurrentRoom, recvcmd.Mid(4), 0);
332 BigMDI->SetStatusText(CurrentRoom, 2);
338 // Reconnect a broken session
340 void CitClient::reconnect_session(void) {
343 CurrentRoom = "__ This is not the name of any valid room __";
345 if (attach(curr_host, curr_port) != 0) {
346 // FIX do this more elegantly
349 sendcmd = "USER " + curr_user;
350 if (serv_trans(sendcmd) != 3) {
351 // FIX do this more elegantly
354 sendcmd = "PASS " + curr_pass;
355 if (serv_trans(sendcmd) != 2) {
356 // FIX do this more elegantly
365 // This is a simple timer that periodically wakes up and sends a NOOP to the
366 // server. This accomplishes two things: it keeps the server connection
367 // alive by trickling some data through when it's otherwise idle, and it allows
368 // the "check for express messages" loop to activate if it has to.
370 keepalive::keepalive(CitClient *sock)
373 which_sock = sock; // Know which instance to refresh
374 Start(15000, FALSE); // Call every 15 seconds
378 void keepalive::Notify(void) {
379 if (which_sock->IsConnected()) {
380 wxString noop = "NOOP";
381 which_sock->serv_trans(noop);