]> code.citadel.org Git - citadel.git/blob - daphne/citclient.cpp
Some HTML/CSS changes to return to a more compact header of the messages with buttons
[citadel.git] / daphne / citclient.cpp
1 // $Id$
2
3 #include "includes.hpp"
4 #include <wx/protocol/protocol.h>
5
6 //
7 //      TRANSPORT LAYER OPERATIONS
8 //
9
10
11
12 // Attach to the Citadel server
13 // FIX (add check for not allowed to log in)
14 int CitClient::attach(wxString host, wxString port) {
15         wxString ServerReady;
16         wxIPV4address addr;
17
18         if (sock->IsConnected())
19         sock->Close();
20
21         addr.Hostname(host);
22         addr.Service(port);
23         sock->SetNotify(0);
24         sock->Connect(addr, TRUE);
25         if (sock->IsConnected()) {
26                 serv_gets(ServerReady);
27                 initialize_session();
28                 curr_host = host;       // Remember host and port, in case
29                 curr_port = port;       // we need to auto-reconnect later
30                 return(0);
31         } else {
32                 return(1);
33         }
34 }
35
36
37 // constructor
38 CitClient::CitClient(void) {
39
40         //wxSocketHandler::Master();
41         sock = new wxSocketClient();
42
43         // The WAITALL flag causes reads to block.  Don't use it.
44         // sock->SetFlags(wxSocketBase::WAITALL);
45
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(wxSOCKET_NOWAIT);
49
50         //wxSocketHandler::Master().Register(sock);
51         // sock->SetNotify(wxSocketBase::REQ_LOST);
52
53         (void)new keepalive(this);
54 }
55
56
57 // destructor
58 CitClient::~CitClient(void) {
59         // Be nice and log out from the server if it's still connected
60         sock->Close();
61 }
62
63
64
65 void CitClient::detach(void) {
66         wxString buf;
67
68         if (sock->IsConnected()) {
69                 serv_puts("QUIT");
70                 serv_gets(buf);
71                 sock->Close();
72         }
73 }
74
75
76
77 // Is this client connected?  Simply return the IsConnected status of sock.
78 bool CitClient::IsConnected(void) {
79         return sock->IsConnected();
80 }
81
82
83
84
85
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;
90         int i;
91         int nl_pos = (-1);
92
93         do {
94                 for (i=nbytes; i>=0; --i)
95                         if (charbuf[i] == 10) nl_pos = i;
96                 if (nl_pos < 0) {
97                         sock->Read(&charbuf[nbytes], (sizeof(charbuf)-nbytes) );
98                         nbytes += sock->LastCount();
99                 }
100                 for (i=nbytes; i>=0; --i)
101                         if (charbuf[i] == 10) nl_pos = i;
102         } while (nl_pos < 0);
103
104         //if (nl_pos != nbytes)
105         //  sock->Unread(&charbuf[nl_pos], nbytes-nl_pos);
106
107         charbuf[nbytes] = 0;
108         charbuf[nl_pos] = 0;
109
110         buf = charbuf;
111         strcpy(charbuf, &charbuf[nl_pos + 1]);
112         nbytes = nbytes - (nl_pos + 1);
113
114 /*
115         GetLine(sock, buf);
116 */
117
118 }
119
120
121
122
123
124
125 // Write a line of text to the server
126 void CitClient::serv_puts(wxString buf) {
127
128         sock->Write((const char *)buf, buf.Len());
129         sock->Write("\n", 1);
130 }
131
132
133
134
135
136
137
138
139
140 //
141 //              SESSION LAYER OPERATIONS
142 //
143
144
145 // Server transaction (returns first digit of server response code)
146 int CitClient::serv_trans(
147                         wxString& command,
148                         wxString& response,
149                         wxString& xferbuf,
150                         wxString desired_room
151                         ) {
152
153         int first_digit, i, pos;
154         wxString buf, pw, junk;
155         bool express_messages_waiting = FALSE;
156
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) {
161                         pw = "";
162                         GotoRoom(desired_room, pw, junk);
163                 }
164         }
165
166
167         // If a mutex is to be wrapped around this function in the future,
168         // it must begin HERE.
169         Critter.Enter();
170         // wxBeginBusyCursor();
171
172         serv_puts(command);
173 /*
174         if (IsConnected() == FALSE) {
175                 wxSleep(20);    // Give a crashed server some time to restart
176                 reconnect_session();
177                 serv_puts(command);
178         } */
179
180         serv_gets(response);
181
182         first_digit = (response.GetChar(0)) - '0';
183
184         if (response.GetChar(3) == '*')
185                 express_messages_waiting = TRUE;
186
187         if (first_digit == 1) {                 // LISTING_FOLLOWS
188                 xferbuf.Empty();
189                 while (serv_gets(buf), buf != "000") {
190                         xferbuf.Append(buf + "\n");
191                 }
192         } else if (first_digit == 4) {          // SEND_LISTING
193                 buf = xferbuf;
194                 while (buf.Length() > 0) {
195                         pos = buf.Find('\n', FALSE);
196                         if ((pos < 0) && (buf.Length() < 250)) {
197                                 serv_puts(buf + "\n");
198                                 buf.Empty();
199                         } else if ((pos < 250) && (pos >= 0)) {
200                                 serv_puts(buf.Left(pos+1));
201                                 buf = buf.Mid(pos+1);
202                         } else {
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);
207                                 } else {
208                                         serv_puts(buf.Left(250) + "\n");
209                                         buf = buf.Mid(pos);
210                                 }
211                         }
212                 }
213                 serv_puts("000");
214         }
215
216         // If a mutex is to be wrapped around this function in the future,
217         // it must end HERE.
218         // wxEndBusyCursor();
219         Critter.Leave();
220
221         if (express_messages_waiting) {
222                 download_express_messages();
223         }
224
225         return first_digit;
226 }
227
228 // Shorter forms of serv_trans()
229 int CitClient::serv_trans(
230                         wxString& command,
231                         wxString& response,
232                         wxString& xferbuf
233                         ) {
234         return serv_trans(command, response, xferbuf, "");
235 }
236
237 int CitClient::serv_trans(wxString& command, wxString& response) {
238         wxString junklist;
239         return serv_trans(command, response, junklist);
240 }
241
242 int CitClient::serv_trans(wxString& command) {
243         wxString junkbuf;
244         return serv_trans(command, junkbuf);
245 }
246
247 //
248 //              PRESENTATION LAYER OPERATIONS
249 //
250
251
252 void CitClient::download_express_messages(void) {
253         wxString sendcmd, recvcmd, x_user, x_sys;
254         wxString xferbuf;
255
256         sendcmd = "GEXP";
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);
261         }
262 }
263
264
265
266 // Set up some things that we do at the beginning of every session
267 void CitClient::initialize_session(void)  {
268         wxString info;
269         wxString sendcmd;
270         wxString recvcmd;
271         int i, pos;
272         wxString *infoptr;
273         wxString infoline;
274
275         CurrentRoom = "";
276
277 #ifdef __WXMSW__
278         sendcmd = "IDEN 0|6|001|Daphne (M$ Windows)";
279 #endif
280
281 #ifdef __WXGTK__
282         sendcmd = "IDEN 0|6|001|Daphne (Unix-GTK)";
283 #endif
284
285 #ifdef __WXMOTIF__
286         sendcmd = "IDEN 0|6|001|Daphne (Unix-Motif)";
287
288 #endif
289
290
291         serv_trans(sendcmd);
292
293         sendcmd = "INFO";
294         if (serv_trans(sendcmd, recvcmd, info)==1) {
295                 i = 0;
296                 while (pos = info.Find('\n', FALSE),  (pos >= 0) ) {
297                         infoline = info.Left(pos);
298                         info = info.Mid(pos+1);
299                         switch(i) {
300
301                         case 0:         SessionID       = atoi(infoline);
302                         case 1:         NodeName        = infoline;
303                         case 2:         HumanNode       = infoline;
304                         case 3:         FQDN            = infoline;
305                         case 4:         ServerSoftware  = infoline;
306                         case 5:         ServerRev       = atoi(infoline);
307                         case 6:         GeoLocation     = infoline;
308                         case 7:         SysAdmin        = infoline;
309                         case 8:         ServerType      = atoi(infoline);
310                         case 9:         MorePrompt      = infoline;
311                         case 10:        UseFloors       = ((atoi(infoline)>0)
312                                                         ? TRUE : FALSE);
313                         case 11:        PagingLevel     = atoi(infoline);
314
315                         ++i;
316                         }
317                 }
318         }
319 }
320
321
322
323 // Goto a room
324
325 bool CitClient::GotoRoom(wxString roomname, wxString password,
326                         wxString& server_response) {
327         int retval;
328         wxString sendcmd, recvcmd;
329
330         sendcmd = "GOTO " + roomname + "|" + password;
331         retval = serv_trans(sendcmd, recvcmd);
332         server_response = recvcmd;
333
334         if (retval != 2) return FALSE;
335
336         extract(CurrentRoom, recvcmd.Mid(4), 0);
337         BigMDI->SetStatusText(CurrentRoom, 2);
338         return TRUE;
339 }
340
341
342
343 // Reconnect a broken session
344
345 void CitClient::reconnect_session(void) {
346         wxString sendcmd;
347
348         CurrentRoom = "__ This is not the name of any valid room __";
349
350         if (attach(curr_host, curr_port) != 0) {
351                 // FIX do this more elegantly
352         }
353
354         sendcmd = "USER " + curr_user;
355         if (serv_trans(sendcmd) != 3) {
356                 // FIX do this more elegantly
357         }
358
359         sendcmd = "PASS " + curr_pass;
360         if (serv_trans(sendcmd) != 2) {
361                 // FIX do this more elegantly
362         }
363 }
364
365
366
367
368
369
370 // This is a simple timer that periodically wakes up and sends a NOOP to the
371 // server.  This accomplishes two things: it keeps the server connection
372 // alive by trickling some data through when it's otherwise idle, and it allows
373 // the "check for express messages" loop to activate if it has to.
374
375 keepalive::keepalive(CitClient *sock)
376         : wxTimer() {
377
378         which_sock = sock;              // Know which instance to refresh
379         Start(15000, FALSE);            // Call every 15 seconds
380 }
381
382
383 void keepalive::Notify(void) {
384         if (which_sock->IsConnected()) {
385                 wxString noop = "NOOP";
386                 which_sock->serv_trans(noop);
387         }
388 }