* added implementations to the general purpose commands.
[citadel.git] / ctdlphp / ctdlprotocol.php
1 <?PHP
2 // $Id$
3 // 
4 // Implements various Citadel server commands.
5 //
6 // Copyright (c) 2003 by Art Cancro <ajc@uncensored.citadel.org>
7 // One program is released under the terms of the GNU General Public License.
8 include "config_ctdlclient.php";
9
10
11 //--------------------------------------------------------------------------------
12 //   internal functions for server communication
13 //--------------------------------------------------------------------------------
14 //
15 // serv_gets() -- generic function to read one line of text from the server
16 //
17 function serv_gets($readblock=FALSE) {
18         global $clientsocket;
19
20         $buf = fgets($clientsocket, 4096);              // Read line
21         $buf = substr($buf, 0, (strlen($buf)-1) );      // strip trailing LF
22         if (CITADEL_DEBUG_CITPROTO == 1) {
23                 if (!$readblock) printf ("<div class='ctdldbgRead'>");
24                 printf($buf);
25                 if (!$readblock) printf ("</div>");
26                 else printf ("<br>");
27         }
28         return $buf;
29 }
30
31 //
32 // serv_puts() -- generic function to write one line of text to the server
33 //
34 function serv_puts($buf) {
35         global $clientsocket;
36         
37         fwrite($clientsocket, $buf . "\n", (strlen($buf)+1) );
38         fflush($clientsocket);
39         if (CITADEL_DEBUG_CITPROTO == 1)
40                 printf ("<div class='ctdldbgWrite'>".$buf."</div>");
41 }
42
43 function read_array() {
44         $nLines = 0;
45         if (CITADEL_DEBUG_CITPROTO == 1)
46             printf ("<div class='ctdldbgRead'>");
47         $buf = serv_gets(TRUE);
48         $ret = array();
49         while (strcasecmp($buf, "000")){
50                 array_push($ret, $buf);
51                 $buf = serv_gets(TRUE);
52                 $nLines++;
53         }
54         if (CITADEL_DEBUG_CITPROTO == 1){
55                 echo "read ".$nLines." lines from the server.";
56                 printf ("</div>");
57         }
58         return $ret;
59 }
60
61
62
63 // 
64 // text_to_server() -- sends a block of text to the server.  Assumes that
65 //                     the server just sent back a SEND_LISTING response code
66 //                     and is now expecting a 000-terminated series of lines.
67 //                     Set 'convert_to_html' to TRUE to convert the block of
68 //                     text to HTML along the way.
69 //
70 function text_to_server($thetext, $convert_to_html) {
71
72         // HTML mode
73         if ($convert_to_html) {
74
75                 // Strip CR's; we only want the LF's
76                 $thetext = trim($thetext, "\r");
77
78                 // Replace hard line breaks with <BR>'s
79                 $thetext = str_replace("\n", "<BR>\n", $thetext);
80
81         }
82
83         // Either mode ... send it to the server now
84         $one_line = strtok($thetext, "\n");
85         while ($one_line !== FALSE) {
86                 $one_line = trim($one_line, "\n\r");
87                 if ($one_line == "000") $one_line = "-000" ;
88                 serv_puts($one_line);
89                 $one_line = strtok("\n");
90         }
91
92         serv_puts("000");       // Tell the server we're done...
93
94         serv_puts("ECHO echo test.");           // FIXME
95         echo "Echo test: " . serv_gets() . "<BR>\n" ;
96
97 }
98
99 //--------------------------------------------------------------------------------
100 //   protocol commands
101 //--------------------------------------------------------------------------------
102
103
104 //
105 // Identify ourselves to the Citadel server (do one once after connection)
106 /* http://www.citadel.org/doku.php/documentation:appproto:connection#iden.identify.the.client.software */
107 //
108 function ctdl_iden($client_info) {
109         global $clientsocket;
110
111         if (count($client_info) != 5)
112                 die("ctdl_iden takes 5 arguments!");
113         // Identify client and hostname
114         serv_puts("IDEN ".implode('|', $client_info));
115         $buf = serv_gets();
116 }
117
118 function ctdl_MessageFormatsPrefered($formatlist){
119         // Also express our message format preferences
120         serv_puts("MSGP ".implode("|", $formatlist));
121         $buf = serv_gets();
122 }
123
124 /* http://www.citadel.org/doku.php/documentation:appproto:connection#noop.no.operation */
125 function ctdl_noop(){
126         // Also express our message format preferences
127         serv_puts("NOOP ");
128         $buf = serv_gets();
129 }
130
131 /* http://www.citadel.org/doku.php/documentation:appproto:connection#quit.quit */
132 function ctdl_quit(){
133         // Also express our message format preferences
134         serv_puts("QUIT ");
135         $buf = serv_gets();
136 }
137
138
139 /* http://www.citadel.org/doku.php/documentation:appproto:connection#mesg.read.system.message */
140 function ctdl_gtls(){
141         // Also express our message format preferences
142         serv_puts("GTLS ");
143         $buf = serv_gets();
144         return $buf;
145 }
146
147
148 /* http://www.citadel.org/doku.php/documentation:appproto:connection#qnop.quiet.no.operation */
149 /* this seems to be dangerous. ask IG
150 function ctdl_qnoop(){
151         // Also express our message format preferences
152         serv_puts("QNOP ");
153 }
154 */
155
156 /* http://www.citadel.org/doku.php/documentation:appproto:connection#echo.echo.something */
157 function ctdl_doecho($echotext){
158         // Also express our message format preferences
159         serv_puts("ECHO ".$echotext);
160         $buf = serv_gets();
161
162 }
163
164 /* http://www.citadel.org/doku.php/documentation:appproto:connection#time.get.server.local.time */
165 /* TODO: what are the other two params? doku is incomplete here. */
166 function ctdl_time(){
167         // Also express our message format preferences
168         serv_puts("TIME");
169         $buf = serv_gets();
170
171 }
172
173
174 /* http://www.citadel.org/doku.php/documentation:appproto:connection#qdir.query.global.directory */
175 function ctdl_qdir($who){
176         // Also express our message format preferences
177         serv_puts("QDIR ".$who);
178         $buf = serv_gets();
179         return array((substr($buf, 0, 1) == "2"), $buf);
180 }
181
182
183 /* http://www.citadel.org/doku.php/documentation:appproto:connection#auto.autocompletion.of.email.addresses */
184 function ctdl_auto($who){
185         // Also express our message format preferences
186         serv_puts("AUTO ".$who);
187         $buf = serv_gets();
188         if (substr($buf, 0, 1) == "1") {
189                 $reply = read_array();
190                 if (count($reply) == 0)
191                         return false;
192                 return $reply;
193         }
194         else
195                 return false;
196 }
197
198
199
200 //
201 // login_existing_user() -- attempt to login using a supplied username/password
202 // Returns an array with two variables:
203 // 0. TRUE or FALSE to determine success or failure
204 // 1. String error message (if relevant)
205 /* http://www.citadel.org/doku.php/documentation:appproto:connection#user.send.user.name */
206 /* http://www.citadel.org/doku.php/documentation:appproto:connection#pass.send.password */
207
208 //
209 function login_existing_user($user, $pass) {
210         global $clientsocket;
211
212         serv_puts("USER " . $user);
213         $resp = serv_gets();
214         if (substr($resp, 0, 1) != "3") {
215                 return array(FALSE, substr($resp, 4));
216         }
217
218         serv_puts("PASS " . $pass);
219         $resp = serv_gets();
220         if (substr($resp, 0, 1) != "2") {
221                 return array(FALSE, substr($resp, 4));
222         }
223
224         $_SESSION["username"] = $user;
225         $_SESSION["password"] = $pass;
226         become_logged_in(substr($resp, 4));
227
228         return array(TRUE, "Login successful.  Have fun.");
229 }
230
231
232 //
233 // create_new_user() -- attempt to create a new user 
234 //                      using a supplied username/password
235 // Returns an array with two variables:
236 // 0. TRUE or FALSE to determine success or failure
237 // 1. String error message (if relevant)
238 //
239 function create_new_user($user, $pass) {
240         global $clientsocket;
241
242         serv_puts("NEWU " . $user);
243         $resp = serv_gets();
244         if (substr($resp, 0, 1) != "2") {
245                 return array(FALSE, substr($resp, 4));
246         }
247
248         serv_puts("SETP " . $pass);
249         $resp = serv_gets();
250         if (substr($resp, 0, 1) != "2") {
251                 return array(FALSE, substr($resp, 4));
252         }
253
254         $_SESSION["username"] = $user;
255         $_SESSION["password"] = $pass;
256         become_logged_in(substr($resp, 4));
257
258         return array(TRUE, "Login successful.  Have fun.");
259 }
260
261
262 //
263 // Code common to both existing-user and new-user logins
264 //
265 function become_logged_in($server_parms) {
266         $_SESSION["logged_in"] = 1;
267
268         $tokens = explode("|", $server_parms);
269         
270         $oneline["username"]   = $tokens[0];
271         $oneline["axlevel"]    = $tokens[1];
272         $oneline["calls"]      = $tokens[2];
273         $oneline["posts"]      = $tokens[3];
274         $oneline["userflags"]  = $tokens[4];
275         $oneline["usernum"]    = $tokens[5];
276         $oneline["lastcall"]   = $tokens[6];
277                 
278         ctdl_goto("_BASEROOM_");
279 }
280
281
282
283 //
284 // Learn all sorts of interesting things about the Citadel server to
285 // which we are connected.
286 /* http://www.citadel.org/doku.php/documentation:appproto:connection#info.get.server.info */
287 //
288 function ctdl_get_serv_info() {
289         serv_puts("INFO");
290         $reply = read_array();
291         if ((count($reply) == 18) &&
292             substr($reply[0], 0, 1) == "1") {
293                 $server_info=array();
294                 $server_info["serv_nodename"]  = $reply[1];
295                 $server_info["serv_humannode"] = $reply[2];
296                 $server_info["serv_fqdn"]      = $reply[3];
297                 $server_info["serv_software"]  = $reply[4];
298                 $server_info["serv_city"]      = $reply[6];
299                 $server_info["serv_sysadmin"]  = $reply[7];
300                 if (CITADEL_DEBUG_CITPROTO == 1)
301                 {
302                         echo "<pre>";
303                         print_r($server_info);
304                         echo "</pre>";
305                 }
306                 return $server_info;
307         }
308         else 
309                 die ("didn't understand the reply to the INFO command");
310
311 }
312
313
314 //
315 // Display a system banner.  (Returns completed HTML.)
316 // (One is probably temporary because it outputs more or less finalized
317 // markup.  For now it's just usable.)
318 //
319 /* http://www.citadel.org/doku.php/documentation:appproto:connection#mesg.read.system.message */
320 function ctdl_mesg($msgname) {
321         global $clientsocket;
322
323         $msgtext = "<DIV ALIGN=CENTER>\n";
324
325         serv_puts("MESG " . $msgname);
326         $response = read_array();
327
328         if (substr($response[0], 0, 1) == "1") {
329                 array_shift($response); // throw away the status code.
330                 $msgtext .= "<TT>" . implode( "</TT><BR>\n" ,$response);
331         }
332         else {
333                 $msgtext .= "<B><I>" . substr($response[0], 4) . "</I></B><BR>\n";
334         }
335
336         $msgtext .= "</DIV>\n";
337         return($msgtext);
338 }
339
340 /* http://www.citadel.org/doku.php/documentation:appproto:connection#mesg.read.system.message */
341 //// TODO: is this still supported?
342 function ctdl_mrtg($what) {
343         global $clientsocket;
344
345         serv_puts("MRTG ".$what);
346         $response = serv_gets();
347
348         if (substr($response, 0, 1) != "1") {
349                 return array(0, NULL);
350         }
351                 
352         $responses = read_array();
353         return $responses;
354 }
355 //
356 // Fetch the list of users currently logged in.
357 /* http://www.citadel.org/doku.php/documentation:appproto:connection#rwho.read.who.s.online */
358 //
359 function ctdl_rwho() {
360         global $clientsocket;
361
362         serv_puts("RWHO");
363         $response = serv_gets();
364
365         if (substr($response, 0, 1) != "1") {
366                 return array(0, NULL);
367         }
368         
369         $all_lines = array();
370         $num_lines = 0;
371         
372         $responses = read_array();
373         foreach ($responses as $response) {
374                 $tokens = explode("|", $response);
375                 $oneline = array();
376
377                 $oneline["session"]      = $tokens[0];          
378                 $oneline["user"]         = $tokens[1];          
379                 $oneline["room"]         = $tokens[2];          
380                 $oneline["host"]         = $tokens[3];          
381                 $oneline["client"]       = $tokens[4];
382                 $oneline["idlesince"]    = $tokens[5];
383                 $oneline["lastcmd"]      = $tokens[6];
384                 $oneline["flags"]        = $tokens[7];
385                 $oneline["realname"]     = $tokens[8];
386                 $oneline["realroom"]     = $tokens[9];
387                 $oneline["realhostname"] = $tokens[10];
388                 $oneline["registered"]   = $tokens[11];
389
390                 // IGnore the rest of the fields for now.
391                 if (CITADEL_DEBUG_CITPROTO == 1)
392                 {
393                         echo "<pre>";
394                         print_r($oneline);
395                         echo "</pre>";
396
397                 }
398
399
400                 $num_lines = array_push($all_lines, $oneline);
401         }
402
403         return array($num_lines, $all_lines);
404
405 }
406
407
408 //
409 // Goto a room.
410 //
411 function ctdl_goto($to_where) {
412         
413         serv_puts("GOTO " . $to_where);
414         $response = serv_gets();
415
416         $results = explode ("|", $response);
417         $status_room = array_shift($results);
418         $status = substr($status_room, 0, 3);
419         if (substr($status, 0, 1) == "2") {
420                 $room = substr($status_room, 4);
421                 array_unshift($results, $room);
422                 $room_state=array(
423                         "state"          => TRUE,
424                         "statereply"     => $status,
425                         "roomname"       => $results[ 0],
426                         "nunreadmsg"     => $results[ 1],
427                         "nmessages"      => $results[ 2],
428                         "rinfopresent"   => $results[ 3],
429                         "flags"          => $results[ 4],
430                         "msgidmax"       => $results[ 5],
431                         "msgidreadmax"   => $results[ 6],
432                         "ismailroom"     => $results[ 7],
433                         "isroomaide"     => $results[ 8],
434                         "nnewmessages"   => $results[ 9],
435                         "floorid"        => $results[10],
436                         "viewselected"   => $results[11],
437                         "defaultview"    => $results[12],
438                         "istrashcan"     => $results[13]);
439                         
440                 $_SESSION["room"] = $room;
441                 if (CITADEL_DEBUG_CITPROTO == 1)
442                 {
443                         echo "<pre>";
444                         print_r($room_state);
445                         echo "</pre>";
446
447                 }
448
449                 return $room_state;
450         }
451
452         else {
453                 return array("state" => FALSE, "statereply" => $status);
454         }
455
456 }
457
458
459
460 //
461 // Fetch the list of known rooms.
462 //
463 function ctdl_knrooms() {
464         global $clientsocket;
465
466         serv_puts("LKRA");
467         $results = read_array();
468
469         if (substr($results[0], 0, 1) != "1") {
470                 return array(0, NULL);
471         }
472         array_shift($results);
473         $all_lines = array();
474         $num_lines = 0;
475
476         foreach ($results as $result){
477                 $oneline = array();
478                 $tokens = explode("|",$result);
479
480                 $oneline["name"]   = $tokens[0];                
481                 $oneline["flags"]  = $tokens[1];                
482                 $oneline["floor"]  = $tokens[2];                
483                 $oneline["order"]  = $tokens[3];                
484                 $oneline["flags2"] = $tokens[4];                
485                 $oneline["access"] = $tokens[5];
486
487                 if ($oneline["access"] & 8) {
488                         $oneline["hasnewmsgs"] = TRUE;
489                 }
490                 else {
491                         $oneline["hasnewmsgs"] = FALSE;
492                 }
493
494                 if (CITADEL_DEBUG_CITPROTO == 1)
495                 {
496                         echo "<pre>";
497                         print_r($oneline);
498                         echo "</pre>";
499
500                 }
501                 $num_lines = array_push($all_lines, $oneline);
502         }
503
504         return array($num_lines, $all_lines);
505
506 }
507
508
509 //
510 // Fetch the list of messages in one room.
511 // Returns: count, response, message array
512 //
513 function ctdl_msgs($mode, $count) {
514         global $clientsocket;
515
516         serv_puts("MSGS " . $mode . "|" . $count);
517         $responses = read_array();
518         print_r($responses);
519
520         $response = array_shift($responses);
521
522         $num_msgs = count($responses);
523         if (substr($response, 0, 1) != "1") {
524                 return array(0, substr($response, 4), NULL);
525         }
526         
527         if (CITADEL_DEBUG_CITPROTO == 1)
528         {
529                 printf("found ".$num_msgs." messages.");
530         }
531         return array($num_msgs, $response, $responses);
532 }
533
534
535 // Load a message from the server.
536 function ctdl_fetch_message($msgnum) {
537         global $clientsocket;
538
539         serv_puts("MSG4 " . $msgnum);
540
541         if (CITADEL_DEBUG_CITPROTO == 1)
542             printf ("<div class='ctdldbgRead'>");
543         $response = serv_gets(TRUE);
544
545         if (substr($response, 0, 1) != "1") {
546                 return array(FALSE, substr($response, 4), NULL);
547         }
548
549         $fields = array();
550         while (strcmp($buf = serv_gets(TRUE), "000")) {
551                 if (substr($buf, 0, 4) == "text") {
552                         if (CITADEL_DEBUG_CITPROTO == 1)
553                                 printf ("</div>\n<h3>Message Body Follows</h3><div class='ctdldbgRead'>");
554                         // We're in the text body.  New loop here.
555                         $fields["text"] = ctdl_msg4_from_server();
556                         if (CITADEL_DEBUG_CITPROTO == 1)
557                                 printf ("</div>");
558                         return array(TRUE, substr($response, 4), $fields);
559                 }
560                 else {
561                         $fields[substr($buf, 0, 4)] = substr($buf, 5);
562                 }
563         }
564
565         // Message terminated prematurely (no text body)
566         return array(FALSE, substr($response, 4), $fields);
567 }
568
569 // Support function for ctdl_fetch_message(). This handles the text body
570 // portion of the message, converting various formats to HTML as
571 // appropriate.
572 function ctdl_msg4_from_server() {
573
574         $txt = "";
575         $msgformat = "text/plain";
576         $in_body = FALSE;
577
578         $previous_line = "";
579         while (strcmp($buf = serv_gets(TRUE), "000")) {
580                 if ($in_body == FALSE) {
581                         if (strlen($buf) == 0) {
582                                 $in_body = TRUE;
583                         }
584                         else {
585                                 if (!strncasecmp($buf, "content-type: ", 14)) {
586                                         $msgformat = substr($buf, 14);
587                                 }
588                         }
589                 }
590                 else {
591                         if (!strcasecmp($msgformat, "text/html")) {
592                                 $txt .= $buf;
593                         }
594                         else if (!strcasecmp($msgformat, "text/plain")) {
595                                 $txt .= "<TT>" . htmlspecialchars($buf) . "</TT><BR>\n" ;
596                         }
597                         else if (!strcasecmp($msgformat, "text/x-citadel-variformat")) {
598                                 if (substr($previous_line, 0, 1) == " ") {
599                                         $txt .= "<BR>\n" ;
600                                 }
601                                 $txt .= htmlspecialchars($buf);
602                         }
603                         else {
604                                 $txt .= htmlspecialchars($buf);
605                         }
606                         $previous_line = $buf;
607                 }
608         }
609
610         return($txt);
611 }
612
613
614 ?>