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