remove ultrix support
[oweals/cde.git] / cde / lib / tt / lib / mp / mp_session.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 //%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                  
24 //%%  (c) Copyright 1993, 1994 International Business Machines Corp.    
25 //%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                   
26 //%%  (c) Copyright 1993, 1994 Novell, Inc.                             
27 //%%  $TOG: mp_session.C /main/13 1999/09/16 13:46:46 mgreess $                                                         
28 /*
29  * @(#)mp_session.C     1.84 96/01/10
30  *
31  * Tool Talk Message Passer (MP) - mp_session.cc
32  *
33  * Copyright (c) 1990,1992 by Sun Microsystems, Inc.
34  *
35  * Implementation of the _Tt_session class.
36  */
37 #include "util/tt_global_env.h"
38 #include "util/tt_xdr_version.h"
39 #include "util/tt_host.h"
40 #include "util/tt_port.h"
41 #include "util/tt_gettext.h"
42 #include "mp/mp_global.h"
43 #include "mp/mp_message.h"
44 #include "mp/mp_mp.h"
45 #include "mp/mp_c_global.h"
46 #include "mp/mp_c_session.h"
47 #include "mp/mp_c_mp.h"
48 #include "mp/mp_pattern.h"
49 #include "mp/mp_rpc_client.h"
50 #include "mp/mp_rpc_interface.h"
51 #include "mp/mp_session.h"
52 #include "mp/mp_desktop.h"
53 #include "mp/mp_xdr_functions.h"
54 #include <unistd.h>
55
56 #ifdef OPT_UNIX_SOCKET_RPC
57 #       include <sys/socket.h>
58 #       include <sys/un.h>
59 #endif // OPT_UNIX_SOCKET_RPC
60
61
62 _Tt_session::
63 _Tt_session()
64 {
65         _env = _TT_ENV_LAST;
66         _is_server = 0;         // default server mode
67         _is_dead = 0;
68         _rpc_version = 0;
69         _pid = 0;
70         _rpc_program = 0;
71         _server_num = 0;
72         _server_uid = 0;
73 }
74
75
76 _Tt_session::
77 ~_Tt_session()
78 {
79         if (! _desktop.is_null()) {
80                 if (_is_server && _env == _TT_ENV_X11 && (_id.len() > 0)) {
81                         if (   (! _desktop->del_prop(TT_XATOM_NAME))
82                             || (! _desktop->del_prop(TT_CDE_XATOM_NAME)))
83                         {
84                                 _tt_syslog( 0, LOG_WARNING,
85                                             catgets( _ttcatd, 1, 14,
86                                                      "could not delete the X "
87                                                      "root window property %s "
88                                                      "advertising this "
89                                                      "session" ),
90                                             TT_XATOM_NAME );
91                         }
92                 }
93         }
94 #ifdef OPT_UNIX_SOCKET_RPC
95         if (_is_server && _socket_name.len()) {
96                 (void)unlink((char *)_socket_name);
97         }
98 #endif
99 }
100
101
102
103 #ifdef OPT_UNIX_SOCKET_RPC
104 // 
105 // Returns the name of the socket file if the unix socket rpc option is
106 // enabled. Note that the name should be unique for all sessions running
107 // on the same machine so it has to be derived from the session id
108 // (which has the required uniqueness properties).
109 // 
110 char * _Tt_session::
111 local_socket_name()
112 {
113 #define SPRFX "/tmp/.TT"
114
115         if (_socket_name.len() == 0) {
116                 char *sname = (char *)malloc(_id.len() + strlen(SPRFX) + 1); 
117                 char *sc;
118
119                 sprintf(sname,"%s%s", SPRFX, (char *)_id);
120                 sc = sname;
121                 while (*sc) {
122                         if (*sc == ' ') {
123                                 *sc = '_';
124                         }
125                         sc++;
126                 }
127                 _socket_name = sname;
128                 free((MALLOCTYPE *)sname);
129         }
130         
131         return((char *)_socket_name);
132 }
133
134 // 
135 // Opens a connection to the unix socket bound to the given socket name.
136 // If successful returns the fd for the connection. Otherwise, returns
137 // -1. 
138 //
139 int
140 c_open_unix_socket(char *socket_name)
141 {
142         int                     sock;
143         struct sockaddr_un      server_addr;
144         sock = socket(AF_UNIX, SOCK_STREAM, 0);
145         if (sock < 0) {
146                 _tt_syslog( 0, LOG_ERR, "c_open_unix_socket(): socket(): %m" );
147                 return -1;
148         }
149         memset(&server_addr, 0, sizeof(server_addr));
150         server_addr.sun_family = AF_UNIX;
151         strcpy(server_addr.sun_path, socket_name);
152 #if defined(_AIX) || defined(hpux)
153         int servlen = strlen(server_addr.sun_path) + sizeof(server_addr.sun_fam\
154 ily);
155         if (connect(sock, (sockaddr *)&server_addr, servlen) < 0) {
156 #else
157         if (connect(sock, (sockaddr *)&server_addr, sizeof(sockaddr_un)) < 0) {
158 #endif
159                 return(-1);
160         }
161
162         return(sock);
163 }
164
165 #endif // OPT_UNIX_SOCKET_RPC
166
167
168 //
169 // Called if an event comes in from our desktop connection.
170 //
171 int  _Tt_session::
172 desktop_event_callback()
173 {
174         if (! _desktop->process_event()) {
175                 return (-1);
176         }
177         else 
178                 return 0;
179 }
180
181
182 // 
183 // Initializes a session with the assumption that the server session is
184 // already running. If the server session isn't running then this method
185 // will return an error as opposed to attempting to autostart a new
186 // session (see _Tt_session::c_init). This is intended as a lower-level
187 // init function used by c_init or whenever autostarting is not required.
188 //
189 // Returns:
190 //      TT_OK
191 //      TT_ERR_ACCESS   Could not init desktop (diagnostic emitted)
192 //      TT_WRN_NOTFOUND No advertised address
193 //      TT_ERR_INVALID  Could not parse advertised address
194 //      TT_ERR_NO_MATCH Advertised address's version is too new
195 //                      (diagnostic emitted)
196 //      TT_ERR_ADDRESS  Could not find advertised host
197 //      TT_ERR_NOMP     Could not init as rpc client
198 // 
199 Tt_status _Tt_session::
200 client_session_init()
201 {               
202         // this is a client of this session. Note that since this
203         // function may be called in the server, we can't use
204         // _tt_mp->in_server().
205         //
206         Tt_status err;
207         int                     rpc_init_done = 0;
208         _Tt_host_ptr            lh;
209
210
211         _is_server = 0;
212         if (env() == _TT_ENV_X11 && _desktop.is_null()) {
213                 _desktop = new _Tt_desktop();
214                 if (! _desktop->init(_displayname, _TT_DESKTOP_X11)) {
215                         return(TT_ERR_ACCESS);
216                 }
217         }
218         if (_address_string.len() == 0) {
219                 if (find_advertised_address(_address_string) != TT_OK) {
220                         return(TT_WRN_NOTFOUND);
221                 }
222         }
223         if (_address_string.len() == 0) {
224                 // Don't call parsed_address() with an empty string,
225                 // or it will think we're trying to create an address
226                 // string instead of parse it.  parsed_address()
227                 // shouldn't be this cute. XXX
228                 return TT_ERR_INVALID;
229         }
230         if ((err = parsed_address(_address_string)) != TT_OK) {
231                 _address_string = (char *)0;
232                 return(err);
233         }
234         if (_TT_AUTH_ICEAUTH == _auth.auth_level() &&
235             (err = _auth.retrieve_auth_cookie()) != TT_OK) {
236                 return err;
237         }
238         if (! _tt_global->find_host(_hostaddr, _host, 1)) {
239                 return(TT_ERR_ADDRESS);
240         }
241
242         if (_id.len() == 0) {
243                 (void)set_id();
244         }
245
246 #ifdef OPT_UNIX_SOCKET_RPC
247         if (_tt_global->get_local_host(lh)) {
248                 // if we are in the same host as the server session
249                 // then we attempt to connect to its advertised unix
250                 // socket to set up an rpc connection. If we succeed
251                 // then we set rpc_init_done to 1 to prevent a normal
252                 // tcp rpc connection from being made.
253                 if (lh->stringaddr() == _host->stringaddr()) {
254                         _u_sock = c_open_unix_socket(local_socket_name());
255                         if (_u_sock != -1) {
256                                 _rpc_client = new _Tt_rpc_client(_u_sock);
257                                 if (_rpc_client->init(lh, _rpc_program,
258                                                       _rpc_version,
259                                                       _server_uid,
260                                                       &_auth)) {
261                                         rpc_init_done = 1;
262                                         // Since we opened the fd ourselves,
263                                         // clnt_destroy won\'t close it
264                                         // unless we tell it it\'s OK.
265                                         clnt_control(_rpc_client->rpc_handle(),
266                                                      CLSET_FD_CLOSE,
267                                                      (char *) NULL);
268                                 }
269                         }
270                 }
271         }
272 #endif  // OPT_UNIX_SOCKET_RPC
273         if (! rpc_init_done) {
274                 _rpc_client = new _Tt_rpc_client();
275                 if (! _rpc_client->init(_host, _rpc_program, _rpc_version,
276                                         _server_uid, _auth)) {
277                         return(TT_ERR_NOMP);
278                 }
279         }
280
281         // Get and save the file descriptor for this session.
282         _tt_mp->save_session_fd(_rpc_client->socket());
283         return(TT_OK);
284 }
285
286
287 // 
288 // Verifies that the server side of the session is alive and is a
289 // ToolTalk session. Currently this is done by first invoking rpc
290 // procedure 0 to see if the session is responding to rpc requests. If
291 // that succeeds then we invoke rpc procedure TT_RPC_VRFY_SESSION which
292 // is a reasonably large number. The reply to this procedure should be
293 // the session id of the session we're talking to. This is a fairly good
294 // (but not foolproof!) method of verifying that this is a ToolTalk
295 // session.
296 //
297 // In fact it seems to be too picky, if the session id we have is
298 // different only in the IP address, it may just be an alternate
299 // IP address for the very same session.  
300 //
301 // Returns:
302 //      TT_OK
303 //      TT_ERR_NOMP     Session unreachable or a hoaxer
304 //      TT_ERR_NO_MATCH Session has wrong version
305 //      TT_ERR_INVALID  Could not parse advertised address
306 //      TT_ERR_AUTHORIZATION    RPC authorization error
307 //
308 Tt_status _Tt_session::
309 ping()
310 {
311         Tt_status       is_live;
312         _Tt_string      sid;
313         
314         // We used to call the NULLPROC here first, but that seems
315         // to be a waste of a round-trip since it doesn't tell
316         // us anything that the TT_RPC_VRFY_SESSION call doesn't.
317
318         is_live = call((int)TT_RPC_VRFY_SESSION,
319                        (xdrproc_t)xdr_void, 0,
320                        (xdrproc_t)tt_xdr_string,
321                        (char *)&sid);
322
323         // We used to string-compare the returned session id with the one we
324         // have, but that was too picky since the IP address can usefully be
325         // different.  Skip the check for now, perhaps we should check at
326         // least some of the components (UNIX pid?)  We already know the
327         // transient RPC number is good, and most everything else is
328         // constant.  We do make a minimal check that the id string is non
329         // null, this might catch the (unlikely) case where the program at
330         // the other end isn't a ttsession but does happen to have a
331         // procedure with a number the same as TT_RPC_VRFY_SESSION,
332         // although in that case I'd expect the RPC to fail in the
333         // XDR routines.
334
335         if (is_live == TT_OK && 0 == sid.len()) {
336                 is_live = TT_ERR_NOMP;
337         }
338
339
340         if (is_live == TT_ERR_UNIMP) {
341                 is_live = TT_ERR_INTERNAL;
342         }
343
344         // And finally, for reasons I haven't quite figured out,
345         // if the session id we have *isn't* equal to the one returned
346         // by ttsession, file scoped messages fail.  I suspect
347         // pattern matching somewhere compares the ids.
348         // So, if the ttsession thinks its name is different, replace
349         // our idea of the name with its idea of its name.
350
351         if (is_live == TT_OK) {
352                 set_id(sid);
353         }
354
355         return(is_live);
356 }
357
358
359 // 
360 // Invokes an rpc call named by the rpc_proc parameter on the server
361 // session and returns any results. If timeout is anything other than -1
362 // then it will be used as the timeout value. Otherwise a suitable
363 // timeout value is chosen. If rebind is 1 then a new rpc connection will
364 // be attempted if this rpc fails.
365 //
366 // Returns:
367 //      TT_OK
368 //      TT_ERR_NOMP     Could not talk to session
369 //      TT_ERR_VERSION_MISMATCH RPC VERSMISMATCH
370 //      TT_ERR_UNIMP    rpc_proc not implemented by session
371 //      TT_ERR_INVALID  Could not parse address
372 //      TT_ERR_AUTHORIZATION    RPC authorization error
373 //
374 Tt_status _Tt_session::
375 call(int rpc_proc,
376      xdrproc_t xdr_arg_fn, char *arg,
377      xdrproc_t xdr_res_fn, char *res,
378      int timeout, int rebind)
379 {
380         //
381         // Ensure global xdr version is set to proper value for this session.
382         //
383         int xdr_version_2_use = _rpc_version;
384         //
385         // Some rpc calls use an xdr version number that is greater
386         // than the version number of the rpc protocol they exist in.
387         // See e.g. _tt_rpc_add_pattern_with_context().
388         //
389         switch (rpc_proc) {
390             case TT_RPC_ADD_PATTERN_WITH_CONTEXT:
391                 if (xdr_version_2_use < TT_CONTEXTS_XDR_VERSION) {
392                         xdr_version_2_use = TT_CONTEXTS_XDR_VERSION;
393                 }
394                 break;
395         }
396         _Tt_xdr_version ver(xdr_version_2_use);
397         Tt_status               status;
398         clnt_stat               rpc_status;
399         int                     processing;
400         int                     retry;
401         int                     tmout;
402
403         if (_rpc_client.is_null()) {
404                 return(TT_ERR_INTERNAL);
405         }
406
407         if (timeout == -1) {
408                 switch (rpc_proc) {
409                       case TT_RPC_HDISPATCH:
410                       case TT_RPC_HUPDATE_MSG:
411                         rebind = 1;
412                         /* fall through */
413                       case TT_RPC_DISPATCH_2:
414                       case TT_RPC_DISPATCH_2_WITH_CONTEXT:
415                       case TT_RPC_UPDATE_MSG_2:
416                       case TT_RPC_MSGREAD_2:
417                         tmout = -1;
418                         break;
419                       default:
420                         tmout = TT_RPC_TMOUT;
421                         break;
422                 }
423         } else {
424                 tmout = timeout;
425         }
426
427         retry = 5;
428         processing = 1;
429         status = TT_ERR_NOMP;
430         while (processing && retry >= 0) {
431                 if (_is_dead) {
432                         // We sometimes hang if we call a dead session
433                         rpc_status = RPC_CANTRECV;
434                         if (! _rpc_client->init(_host, _rpc_program,
435                                                 _rpc_version,
436                                                 _server_uid,
437                                                 _auth)) {
438                                 status = TT_ERR_NOMP;
439                                 processing = 0;
440                                 break;
441                         }
442
443                 } else {
444                         rpc_status = _rpc_client->call(rpc_proc, xdr_arg_fn,
445                                                        arg, xdr_res_fn, res,
446                                                        tmout);
447                 }
448                 switch (rpc_status) {
449                       case RPC_SUCCESS:
450                         status = TT_OK;
451                         processing = 0;
452                         break;
453                       case RPC_CANTRECV:
454                         _is_dead = 1;
455                         status = TT_ERR_NOMP;
456                         retry--;
457                         break;
458                       case RPC_VERSMISMATCH:
459                       case RPC_PROGVERSMISMATCH:
460                         status = TT_ERR_VERSION_MISMATCH;
461                         processing = 0;
462                         break;
463                       case RPC_TIMEDOUT:
464                         if (tmout == 0 || tmout == -1) {
465                                 // rpc always returns a timeout error if we're
466                                 // in message-passing mode
467                                 status = TT_OK;
468                         } else {
469                                 // network errors
470                                 status = TT_ERR_NOMP;
471                         }
472                         processing = 0;
473                         break;
474                       case RPC_CANTSEND:
475                         if (!rebind) {
476                                 status = TT_ERR_NOMP;
477                                 retry--;
478                                 break;
479                         }
480                         // try to rebind _rpc_client and try again
481                         if (! _rpc_client->init(_host, _rpc_program,
482                                                 _rpc_version,
483                                                 _server_uid,
484                                                 _auth)) {
485                                 status = TT_ERR_NOMP;
486                                 processing = 0;
487                                 break;
488                         }
489                         continue;
490                       case RPC_PROCUNAVAIL:
491                         // We get this if there is a mild version 
492                         // mismatch, e.g. a 1.1 library talking to 
493                         // a 1.0.x server, and one of the new
494                         // API calls is used.  This is relatively
495                         // benign, just let the user know that
496                         // his call is unimplemented in this system
497                         status = TT_ERR_UNIMP;
498                         processing = 0;
499                         break;
500                       case RPC_AUTHERROR:
501                         status = TT_ERR_AUTHORIZATION;
502                         processing = 0;
503                         break;
504                       default:
505                         status = TT_ERR_INTERNAL;
506                         processing = 0;
507                         break;
508                 }
509         }
510
511         if (status == TT_ERR_NOMP &&
512             !_tt_mp->in_server() &&
513             !_tt_c_mp->default_c_session.is_null() &&
514             has_id(_tt_c_mp->default_c_session->address_string())) {
515                 _tt_c_mp->default_c_session = NULL;
516         }
517
518         return(status);
519 }
520
521
522 // 
523 // Attempts to find the address of the server session. This is done
524 // according to what kind of environment or session type we're in. 
525 //
526 Tt_status _Tt_session::
527 find_advertised_address(_Tt_string &session_addr)
528 {
529         switch (env()) {
530               case _TT_ENV_X11:
531                 if ((! _desktop->get_prop(TT_CDE_XATOM_NAME, session_addr)) &&
532                     (! _desktop->get_prop(TT_XATOM_NAME, session_addr))) {
533                         return(TT_ERR_NOMP);
534                 }
535                 return(TT_OK);
536               case _TT_ENV_PROCESS_TREE:
537                 if (_address_string.len() == 0) {
538                         session_addr = _tt_get_first_set_env_var(2, TT_CDE_XATOM_NAME, TT_XATOM_NAME);
539                 } else {
540                         session_addr = _address_string;
541                 }
542                 return(TT_OK);          
543               case _TT_ENV_LAST:
544               default:
545                 return(TT_ERR_NOMP);
546         }
547 }
548
549
550 //
551 // Returns a number indicating what type of session we're in.
552 //
553 _Tt_env _Tt_session::
554 env()
555 {
556         if (_tt_mp->in_server()) {
557                 if (_env == _TT_ENV_LAST && getenv("DISPLAY")) {
558                         _env = _TT_ENV_X11;
559                 }
560                 return(_env);
561         } else if (_env != _TT_ENV_LAST) {
562                 return(_env);
563         } else {
564                 if (_tt_get_first_set_env_var(2, TT_CDE_XATOM_NAME, TT_XATOM_NAME)) {
565                         return(_env = _TT_ENV_PROCESS_TREE);
566                 } else if (getenv("DISPLAY")) {
567                         return(_env = _TT_ENV_X11);
568                 } else {
569                         return(_TT_ENV_LAST);
570                 }
571         }
572 }
573
574
575 // 
576 // Sets the session-type for the session. This affects how the session id
577 // is advertised to potential clients. 
578 // 
579 void _Tt_session::
580 set_env(_Tt_env session_env, _Tt_string arg)
581 {
582         _env = session_env;
583         switch (_env) {
584               case _TT_ENV_X11:
585                 _displayname = arg;
586                 break;
587               default:
588                 break;
589         }
590 }
591
592
593
594 // 
595 // Returns the ToolTalk session id for the X session named by xdisp. Note
596 // that no actual contact is attempted.
597 // 
598 _Tt_string _Tt_session::
599 Xid(_Tt_string xdisp)
600 {
601         return _desktop->session_name(xdisp);
602 }
603
604 /*
605 void _Tt_session::
606 print(FILE *fs) const
607 {
608         fprintf(fs,"_Tt_session::\n");
609 }
610 */
611
612 //
613 // Used to set or get the address string of a session as well as set
614 // appropiately the _rpc_version field of the session. If addr_string is
615 // null, then parsed_address will set the address string of the session.
616 // In this case, it sets _rpc_version to the default TT_RPC_VERSION. If
617 // addr_string is not null then parsed_address will attempt to parse the
618 // string and extract the information in it. In this case, if the
619 // addr_string happens to be an fcs1 version of session addresses then
620 // _rpc_version is set to 1, otherwise it is set to TT_RPC_VERSION.
621 //
622 // Note that the intended convention for address strings is that only the
623 // information that is known to this method will be parsed out of them.
624 // If there is any information beyond that then it is left uninterpreted.
625 // This means that future ToolTalk versions are free to append
626 // information to the end of an address string (as is already done
627 // below) without compromising compatibility with older clients.
628 //
629 // Returns:
630 //      TT_OK
631 //      TT_ERR_INVALID  Could not parse address
632 //      TT_ERR_NO_MATCH Address version is too new (diagnostic emitted)
633 //
634 Tt_status _Tt_session::
635 parsed_address(_Tt_string &addr_string)
636 {
637 #define IPVADDRLEN      16
638         char            session_host[IPVADDRLEN];
639         char            strid[BUFSIZ];
640         int             junk_version = 1;
641         Tt_status       status;
642         const char      *addr_format_fmt = "%%ld %%d %%d %%d %%lu %%%ds %%d";
643         char             addr_format[32];
644         const char      *fcs1_addr_format_fmt = "%%ld %%d %%d %%d %%lu %%%ds";
645         char             fcs1_addr_format[32];
646         
647         //
648         // Note: the fcs 1.0 version of tooltalk uses the first 6
649         // fields in addr_format. Changing these first 6 fields
650         // compromises binary compatibility with the 1.0 version.
651         // Additional fields can be added at the end of addr_format as
652         // long as if the parsing fails then the old format
653         // (fcs1_addr_format) is tried.
654         //
655         _rpc_version = TT_RPC_VERSION;
656         if (addr_string.len() == 0) {
657                 int ipaddr_len;
658
659                 // set address string
660                 if (_host.is_null()) {
661                         return(TT_ERR_INTERNAL);
662                 }
663                 if (_host->stringaddr().len() == 0) {
664                         return(TT_ERR_INTERNAL);
665                 }
666
667                 ipaddr_len = _host->stringaddr().len();
668                 if (ipaddr_len > IPVADDRLEN) ipaddr_len = IPVADDRLEN;
669
670                 sprintf(fcs1_addr_format, fcs1_addr_format_fmt, ipaddr_len);
671                 sprintf(addr_format,      addr_format_fmt,      ipaddr_len);
672
673                 // put version number for format first (this is so we
674                 // can change the format later)
675                 sprintf(strid, "%02d ", _TT_XATOM_VERSION);
676                 sprintf(strid+3, addr_format,
677                         (long)_pid,
678                         _rpc_program,
679                         junk_version,
680                         (int)_auth.auth_level(),
681                         (long)_server_uid,
682                         (char *)_host->stringaddr(),
683                         _rpc_version);
684                 
685                 addr_string = strid;
686                 if ((status = _auth.set_sessionid(
687                                                 _rpc_program,
688                                                 _auth.auth_level(),
689                                                 _host->stringaddr(),
690                                                 _rpc_version)) != TT_OK) {
691                     _tt_syslog(0, LOG_ERR,"_auth.set_sessionid() != TT_OK");
692                     return(status);
693                 }
694         } else {
695                 // get (parse) address string
696                 char *str = (char *)addr_string;
697
698                 sprintf(fcs1_addr_format, fcs1_addr_format_fmt, IPVADDRLEN);
699                 sprintf(addr_format,      addr_format_fmt,      IPVADDRLEN);
700
701                 // check version number of format first
702
703                 // XXX: note that if _TT_XATOM_VERSION is ever changed
704                 // then backward compatibility is seriously
705                 // compromised. 
706
707                 if (atoi(str) != _TT_XATOM_VERSION) {
708                         _tt_syslog( 0, LOG_ERR,
709                                     catgets( _ttcatd, 1, 15,
710                                              "address version is %d, but "
711                                              "I only understand %d! (address: "
712                                              "<%s>)" ),
713                                     atoi(str), _TT_XATOM_VERSION,
714                                     addr_string.operator const char *() );
715                         return TT_ERR_NO_MATCH;
716                 }
717                 
718                 // XXX: Originally, the fcs1_addr_format put in a
719                 // field describing the rpc version of the server.
720                 // However, the fcs1 version of client_session_init
721                 // erroneously initialized the rpc connection with the
722                 // *server* rpc version rather than its own. Thus we
723                 // can't use the rpc version field in fcs1_addr_format
724                 // because then old fcs1.0 clients would mistakenly
725                 // initialize themselves as version 1+ clients. Thus
726                 // (sigh) we have to add a field at the end of
727                 // fcs1_addr_format that is the real rpc version of
728                 // the server. This is the reason that the first rpc
729                 // version parsed from addr_format is thrown away. 
730
731                 long long_pid;
732                 long long_server_uid;
733                 _Tt_auth_level auth_level;
734                 if (7 != sscanf(str+3, addr_format,
735                                 &long_pid,
736                                 &_rpc_program,
737                                 &junk_version, /* always 1 ... */
738                                 &auth_level,
739                                 &long_server_uid,
740                                 session_host,
741                                 &_rpc_version)) {
742
743                         // new format scan failed. Try to parse the
744                         // string for the old format
745
746                         if (6 != sscanf(str+3, fcs1_addr_format,
747                                         &long_pid,
748                                         &_rpc_program,
749                                         &_rpc_version,
750                                         &auth_level,
751                                         &long_server_uid,
752                                         session_host)) {
753                                 _pid = (pid_t) long_pid;
754                                 _server_uid = (uid_t) long_server_uid;
755                                 return(TT_ERR_INVALID);
756                         } else {
757                                 _rpc_version = 1;
758                         }
759                 }
760                 _pid = (pid_t) long_pid;
761                 _server_uid = (uid_t) long_server_uid;
762                 _hostaddr = session_host;
763                 _auth.set_auth_level(auth_level);
764                 if ((status = _auth.set_sessionid(
765                                 _rpc_program,
766                                 auth_level,
767                                 _hostaddr,
768                                 _rpc_version)) != TT_OK) {
769                     _tt_syslog(0, LOG_ERR,"_auth.set_sessionid() != TT_OK");
770                     return(status);
771                 }
772         }
773
774         return(TT_OK);
775 }
776
777
778 // 
779 // Sets the id of a session. Note that addressing information (the rpc
780 // number and version) are not part of the id. This makes it possible in
781 // the future to provide server address rebinding.
782 // 
783 Tt_status _Tt_session::
784 set_id(char *id)
785 {
786         char                    host[64];
787         char                    dpname[125];
788         int                     svnum;
789         char                    *ssid;
790         
791         if (id != (char *)0) {
792                 _id = id;
793                 switch (_id[0]) {
794                       case 'X':
795                         // construct a suitable DISPLAY name from the id
796                         // which should have the format "X <host> <server_num>"
797                         if (sscanf((char *)id, "X %s %d", host, &svnum) != 2) {
798                                 return(TT_ERR_SESSION);
799                         }
800                         /* We _cannot_ set _displayname based solely on host and svnum, 
801                          * because :0 is NOT the same as 127.0.0.1:0 as far as X11
802                          * is concerned: by default, it will only accept connections
803                          * to the former. (XOpenDisplay etc. will fail if you try the below!)
804                         sprintf(dpname, "%s:%d", host, svnum);
805                         _displayname = dpname;
806                          */
807                         if (! _displayname.len()) {
808                                 _displayname = _tt_global->xdisplayname;
809                         }
810
811                         _server_num = svnum;
812                         _env = _TT_ENV_X11;
813                         break;
814                       case 'P':
815                         // A process-tree session id is just an address string
816                         // preceded by "P ".
817                         _address_string = _id.mid(2, _id.len());
818                         _env = _TT_ENV_PROCESS_TREE;
819                         break;
820                       default:
821                         return(TT_ERR_SESSION);
822                 }
823                 
824         } else {
825                 // set the "id" which is the publicly visible *logical*
826                 // address of this session.
827                 ssid = _tt_get_first_set_env_var(2, TT_CDE_START_SID, TT_START_SID);
828                 if (ssid != (char *)0) {
829                         _id = ssid;
830                 } else {
831                         switch (env()) {
832                               case _TT_ENV_X11:
833                                 _type = "X";
834                                 if (! _displayname.len()) {
835                                         _displayname = _tt_global->xdisplayname;
836                                 }
837                                 _id = Xid(_displayname);
838                                 break;
839                               case _TT_ENV_PROCESS_TREE:
840                                 _type = "P";
841                                 _server_num = _pid;
842                                 // sami said originally:
843                                 // we need to include the address string in
844                                 // the id of a process-tree session because
845                                 // there is no alternate means of finding
846                                 // the address (ie. for X11 the id could be
847                                 // rid of address info because the X server
848                                 // served as the alternate way of finding
849                                 // the address).
850                                 // rfm sez, 16 June 94:
851                                 // Of course that alternate way was a bad
852                                 // idea since it means other users can't
853                                 // send messages using the session because
854                                 // they can't contact the X server.
855                                 _id = process_tree_id();
856                                 break;
857                               case _TT_ENV_LAST:
858                               default:
859                                 return(TT_ERR_INTERNAL);
860                         }
861                 }
862         }
863         
864         return(TT_OK);
865 }
866
867
868 // 
869 // Returns a process-tree id for this session. This is essentially just
870 // the address of the session preceded by a "P". The purpose of this is
871 // that whenever this exact session needs to be advertised to a
872 // persistent medium (as is done with file-scope messages) this version
873 // of the session id is used so that if the session goes down and comes
874 // up again, the session id stored on disk will become stale.
875 // 
876 _Tt_string _Tt_session::
877 process_tree_id()
878 {
879         _Tt_string      result = "P ";
880
881         return(result.cat(_address_string));
882 }
883
884
885 bool_t _Tt_session::
886 xdr(XDR *xdrs)
887 {
888         return(_id.xdr(xdrs) && _address_string.xdr(xdrs));
889 }
890
891
892 _Tt_string _Tt_session::
893 address_string()
894 {
895         return(_address_string);
896 }
897
898 _Tt_string _Tt_session::
899 displayname()
900 {
901         return(_displayname);
902 }
903
904
905 _Tt_string
906 _tt_session_address(_Tt_object_ptr &o)
907 {
908         return ((_Tt_session *)o.c_pointer())->address_string();
909 }
910
911
912 // 
913 // Returns 1 if the given id is the same as the id of this session or if
914 // it is a process-tree id then it has the same address as this session.
915 // 
916 int _Tt_session::
917 has_id(const _Tt_string &id)
918 {
919         if (id == _id) {
920                 return(1);
921         }
922
923         if (id.len() == 0) {
924                 return(0);
925         }
926
927         if (id == _address_string) {
928                 return(1);
929         }
930
931         if (id[0] == 'P') {
932                 _Tt_string      addr = id.mid(2,id.len());
933                 return(addr == _address_string);
934         }
935
936         return(0);
937 }
938
939
940 // ...for strings list
941 int _Tt_session::
942 has_id(const _Tt_string_list_ptr slist_p)
943 {
944         _Tt_string_list_cursor c(slist_p);
945
946         while (c.next()) {
947                 if (has_id(*c)) {
948                         return(1);
949                 }
950         }
951         return(0);
952 }