165369868d19d9307a6cc0821445545a5ed641cf
[oweals/cde.git] / cde / lib / tt / lib / api / c / api_filemap.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 //%%  $XConsortium: api_filemap.C /main/3 1995/10/23 09:52:27 rswiston $                                                        
28
29 /*
30  *
31  * @(#)api_filemap.C    1.21 95/02/21
32  *
33  * Copyright (c) 1990, 1993 by Sun Microsystems, Inc.
34  *
35  * THis file implements the api filename mapping calls to map to/from
36  * canonical name formats of the form hostname:pathname to an
37  * absolute pathname of the form /pathname.
38  */
39
40 #include <unistd.h>
41 #include <sys/param.h>
42 #include "api/c/api_filemap.h"
43 #include "db/tt_db_file.h"
44 #include "mp/mp_c.h"
45 #include "util/tt_path.h"
46 #include "api/c/tt_c.h"
47 #include "api/c/api_api.h"
48 #include "api/c/api_mp.h"
49 #include "api/c/api_error.h"
50 #include "util/tt_audit.h"
51 #include "util/tt_port.h"
52 #include "util/tt_host_equiv.h"
53
54 // "magic" (in the /etc/magic sense) prefix for netfile strings
55 #define TT_NETFILE_PREFIX "TTN0"
56
57 // Include COMPATIBILITY code for earlier CDE snapshots
58
59 #define CDE_SNAPSHOT_COMPATIBILITY 1
60
61 char * _tt_host_file_netfile(const char * host, const char * filename);
62 char * _tt_host_netfile_file(const char * host, const char * netfilename);
63 char * _tt_file_netfile(const char * filename);
64 char * _tt_netfile_file(const char * netfilename);
65 static int get_keyword_value(_Tt_string s, const char * keyword,
66                              int &start, int &end);
67
68 /******************************************************************/
69 /* filename mapping API calls implementation.                     */
70 /******************************************************************/
71
72 // On the specified host, assemble a netfilename for the specified filename.
73 char *
74 tt_host_file_netfile(const char * host, const char * filename)
75 {
76         _Tt_audit audit;
77         Tt_status status = audit.entry("CC", TT_HOST_FILE_NETFILE, host, filename);
78         char *result;
79
80         if (status != TT_OK) {
81                 audit.exit((char *)_tt_error_pointer(status));
82                 return (char *)_tt_error_pointer(status);
83         }
84
85         result = _tt_host_file_netfile(host, filename);
86         audit.exit(result);
87
88         return result;
89 }
90
91
92 // On the specified host, create a filename for the specified netfilename.
93 char *
94 tt_host_netfile_file(const char * host, const char * netfilename)
95 {
96         _Tt_audit audit;
97         Tt_status status = audit.entry("CC", TT_HOST_NETFILE_FILE, host, netfilename);
98         char *result;
99
100         if (status != TT_OK) {
101                 audit.exit((char *)_tt_error_pointer(status));
102                 return (char *)_tt_error_pointer(status);
103         }
104
105         result = _tt_host_netfile_file(host, netfilename);
106         audit.exit(result);
107
108         return result;
109 }
110
111
112 // On the local host, create a netfilename for the specified filename.
113 char *
114 tt_file_netfile(const char *filename)
115 {
116         _Tt_audit audit;
117         Tt_status status = audit.entry("C", TT_FILE_NETFILE, filename);
118         char *result;
119
120         if (status != TT_OK) {
121                 audit.exit((char *)_tt_error_pointer(status));
122                 return (char *)_tt_error_pointer(status);
123         }
124
125         result = _tt_file_netfile(filename);
126         audit.exit(result);
127
128         return result;
129 }
130
131
132 // On the local host, create a filename for the specified netfilename.
133 char *
134 tt_netfile_file(const char *netfilename)
135 {
136         _Tt_audit audit;
137         Tt_status status = audit.entry("C", TT_NETFILE_FILE, netfilename);
138         char *result;
139
140         if (status != TT_OK) {
141                 audit.exit((char *)_tt_error_pointer(status));
142                 return (char *)_tt_error_pointer(status);
143         }
144
145         result = _tt_netfile_file(netfilename);
146         audit.exit(result);
147
148         return result;
149 }
150
151
152 // Do the following:
153 //
154 //      1) Make RPC call to remote host, and on that
155 //         host run tt_file_netfile(filename).
156 //
157 //      2) Return the result here.
158 char *
159 _tt_host_file_netfile(const char * host, const char * filename)
160 {
161         Tt_status       status;
162         _Tt_string      hostname(host);
163         _Tt_string      local_host = _tt_gethostname();
164
165         _tt_internal_init();
166
167         // If this is an RPC call to our localhost, do
168         // it directly and don't bother with RPC.
169
170         _Tt_host_equiv_ptr eq_p = new _Tt_host_equiv;
171
172         if (eq_p->hostname_equiv(hostname, local_host) == 1) {
173 #ifdef notdef
174 printf("DEBUG _tt_host_file_netfile: resolving locally.\n");
175 #endif
176
177                 // strdup already done in _tt_netfile_file()
178                 return _tt_file_netfile(filename);
179         }
180
181         _Tt_string      path(filename);
182         _Tt_string      netfile;
183
184         // Connect to dbserver on remote host.
185         _Tt_db_results  db_status;
186         _Tt_db_client_ptr h_dbserv = new _Tt_db_client(hostname, db_status);
187
188         // run _tt_file_netfile() on the remote host.
189         if ((status = _tt_get_api_error(h_dbserv->getConnectionResults(),
190                                           _TT_API_FILE_MAP)) == TT_OK) {
191
192                 status = _tt_get_api_error(h_dbserv->file_netfile(path, netfile),
193                                            _TT_API_FILE_MAP);
194         }
195
196         if (status != TT_OK) {
197                 return (char *)_tt_error_pointer(status);
198         }
199
200 #if CDE_SNAPSHOT_COMPATIBILITY
201         // For compatibility with development snapshots of CDE,
202         // if the returned netfile does not start with TT_NETFILE_PREFIX,
203         // put it on.
204
205         if (netfile.left(strlen(TT_NETFILE_PREFIX)) != TT_NETFILE_PREFIX) {
206                 path = netfile;
207                 netfile = TT_NETFILE_PREFIX;
208                 netfile = netfile.cat(path);
209         }
210 #endif /* CDE_SNAPSHOT_COMPATIBILITY */ 
211
212         return _tt_strdup((char *) netfile);
213 }
214
215
216 // Do the following:
217 //
218 //      1) Make RPC call to remote host, and on that
219 //         host run tt_netfile_file(netfilename).
220 //
221 //      2) Return the result here.
222 char *
223 _tt_host_netfile_file(const char * host, const char * netfilename)
224 {
225         Tt_status       status;
226         _Tt_string      hostname(host);
227         _Tt_string      local_host = _tt_gethostname();
228
229         _tt_internal_init();
230
231         // If this is an RPC call to our localhost, do
232         // it directly and don't bother with RPC. 
233          
234         _Tt_host_equiv_ptr      eq_p = new _Tt_host_equiv;
235
236         if (eq_p->hostname_equiv(hostname, local_host) == 1) {
237 #ifdef notdef
238 printf("DEBUG _tt_host_netfile_file: resolving locally.\n");
239 #endif
240
241                 // strdup already done in _tt_netfile_file()
242                 return _tt_netfile_file(netfilename);
243         }
244
245         _Tt_string      path(netfilename);
246         _Tt_string      file;
247
248
249         // Connect to dbserver on remote host.
250         _Tt_db_results  db_status;
251         _Tt_db_client_ptr h_dbserv = new _Tt_db_client(hostname, db_status);
252
253         // run _tt_netfile_file() on the remote host.
254         if ((status = _tt_get_api_error(h_dbserv->getConnectionResults(),
255                                         _TT_API_FILE_MAP)) != TT_OK) {
256                 status = status == TT_ERR_PATH ? TT_ERR_NETFILE : status;
257                 return (char *)_tt_error_pointer(status);
258         }
259         
260         if ((status = _tt_get_api_error(h_dbserv->netfile_file(path, file),
261                                         _TT_API_FILE_MAP)) == TT_OK) {
262                 return _tt_strdup((char *) file);
263         }
264
265 #if CDE_SNAPSHOT_COMPATIBILITY
266         // Mapping failed.  It's just possible that it's a dbserver from
267         // an earlier development snapshot which doesn't understand
268         // netfiles with TT_NETFILE_PREFIX in front of them.
269
270
271         if (path.left(strlen(TT_NETFILE_PREFIX)) == TT_NETFILE_PREFIX) {
272                 path = path.right(path.len()-strlen(TT_NETFILE_PREFIX));
273                 
274                 if ((status = _tt_get_api_error(h_dbserv->netfile_file(path, file),
275                                         _TT_API_FILE_MAP)) == TT_OK) {
276                         return _tt_strdup((char *) file);
277                 }
278         }
279
280 #endif /* CDE_SNAPSHOT_COMPATIBILITY */
281
282         status = status == TT_ERR_PATH ? TT_ERR_NETFILE : status;
283         return (char *)_tt_error_pointer(status);       
284
285 }
286
287
288 // given a filename in a valid file path format,
289 // i.e. /a/b/..., return a string in the canonical
290 // format of hostname:/x/y/....
291 //
292 char *
293 _tt_file_netfile(const char *filename)
294 {
295         _Tt_string path(filename);
296
297         _tt_internal_init();
298
299         // Create the canonical filename.
300         _Tt_api_filename_map_ptr mapp = new _Tt_api_filename_map;
301         return _tt_strdup(mapp->set_filename(path));
302 }
303
304
305 // given a filename in the canonical format, i.e.
306 // hostname:/x/y/.... return a string in the local
307 // file path format, i.e. /a/b/...
308 //
309 char *
310 _tt_netfile_file(const char *netfilename)
311 {
312         _Tt_string               network_path(netfilename), result;
313         _Tt_api_filename_map_ptr original_mapp = new _Tt_api_filename_map;
314         _Tt_api_filename_map_ptr new_mapp = new _Tt_api_filename_map;
315
316         _tt_internal_init();
317
318         // We want to turn the rpath into a local path.  We'd prefer
319         // to use the same local path the creator of the netfile used,
320         // if that works and means the same file.  We do this by a two
321         // step process:
322         //
323         //      1) generate a new rpath based on the original lpath.
324         //         If the new rpath == the original rpath, use the
325         //         original lpath.
326         //
327         //      2) If the new rpath isn't the same, then turn
328         //         the original rpath into a lpath by trudging through
329         //         the mount tables.
330
331         // parse the canonical filename into its' component parts.
332         result = original_mapp->parse_netfilename(network_path);
333         if (result.len() == 0) {
334                 return (char *)_tt_error_pointer(TT_ERR_NETFILE);
335         }
336
337         // Do step 1 above: generate new rpath from old lpath.
338         new_mapp->set_filename(original_mapp->lpath_val());
339
340         _Tt_string host = new_mapp->hostname_val();
341         _Tt_string rpath = new_mapp->rpath_val();
342         _Tt_string lpath = new_mapp->lpath_val();
343
344         // compare the new rpath with the old rpath.
345         if (host == original_mapp->hostname_val()) {
346                 if (rpath == original_mapp->rpath_val()) {
347                         return _tt_strdup(original_mapp->lpath_val());
348                 }
349         }
350
351         // Do step 2 above: convert the original rpath to a local path.
352         result = _tt_network_path_to_local_path(original_mapp->hostname_val().cat(':').cat(original_mapp->rpath_val()));
353
354         return _tt_strdup(result);
355 }
356
357
358 /******************************************************************/
359 /* _Tt_api_filename_map class implementation below.               */
360 /******************************************************************/
361
362 // A canonical pathname is of the following form:
363 // "HOST=0-x,RPATH=x-y,LPATH=y-z:HostnameLpathRpath", where
364 // x, y, and z are ASCII representations of the number
365 // which is the position within the character array where
366 // the concatenated strings begin and end.
367
368 // "pathname" should be an absolute or relative pathname.
369 _Tt_string _Tt_api_filename_map::
370 set_filename(const _Tt_string & filename)
371 {
372         _Tt_string      absolute_path = filename;
373         _Tt_string      tmp_string(MAXPATHLEN * 3);
374         int             i;
375
376         if (filename[0] != '/') {
377         // A relative path, make it absolute.
378                 char wd[MAXPATHLEN+1];
379
380                 if (getcwd(wd, sizeof(wd))) {
381                         absolute_path = _Tt_string(wd).cat("/").cat(filename);
382                 }
383         }
384
385         // Now get the network path of the file.
386         tmp_string = _tt_local_network_path(absolute_path);
387
388 #ifdef notdef
389 printf("DEBUG set_filename: _tt_local_network_path(%s) returned %s\n",
390         (char *) absolute_path, (char *) tmp_string);
391 #endif
392
393         _lpath = absolute_path; // what we know the file as.
394
395         // load hostname and rpath simultaneously
396         _rpath = tmp_string.split(':', _hostname);
397
398         // construct a canonical pathname
399         _canonical_path = TT_NETFILE_PREFIX;
400         _canonical_path = _canonical_path.cat("HOST=0-").cat(_hostname.len() - 1).cat(',');
401         i = _hostname.len();
402
403         _canonical_path =
404         _canonical_path.cat("RPATH=").cat(i).cat('-').cat(i + _rpath.len() - 1).cat(',');
405         i += _rpath.len();
406
407         _canonical_path =
408         _canonical_path.cat("LPATH=").cat(i).cat('-').cat(i + _lpath.len() - 1).cat(':');
409
410         _canonical_path = _canonical_path.cat(_hostname).cat(_rpath).cat(_lpath);
411
412 #ifdef notdef
413 printf("DEBUG set_filename: _canonical_path == %s\n", (char *) _canonical_path);
414 #endif
415
416         return _canonical_path;
417 }
418
419
420 // "canonical_name" is of the format
421 // "TTN0HOST=0-x,RPATH=x-y,LPATH=y-z:HostnameLpathRpath" as described above
422 // If not, parse_netfilename returns (_Tt_string)0.
423 _Tt_string _Tt_api_filename_map::
424 parse_netfilename(const _Tt_string & canonical_name)
425 {
426         _Tt_string      tmp_string, dummy;
427         int             i, h_begin, h_end, l_begin, l_end, r_begin, r_end;
428
429         // parse the canonical_path.
430
431         // It's important to do this in a way that is independent of the
432         // order of the keywords, and which ignores any extraneous keywords
433         // which may be introduced later.
434
435         // A canonical name should start with TT_NETFILE_PREFIX ("TTN0"),
436         // which we ignore.  However if CDE_SNAPSHOT_COMPATIBILITY is 1, and
437         // it starts with HOST= it's from
438         // a slightly older version of the code, and we accept that too.
439
440         i = strlen(TT_NETFILE_PREFIX);
441         if (canonical_name.left(i) == TT_NETFILE_PREFIX) {
442                 tmp_string = canonical_name.mid(i, canonical_name.len()-i);
443 #if CDE_SNAPSHOT_COMPATIBILITY
444         } else if (canonical_name.left(5) == "HOST=") {
445                 tmp_string = canonical_name;
446 #endif /* CDE_SNAPSHOT_COMPATIBILITY */         
447         } else {
448                 return (_Tt_string) 0;
449         }
450
451         // All the keyword stuff is before the colon.
452         
453         tmp_string.split(':',tmp_string);
454
455         if (!(get_keyword_value(tmp_string, "HOST", h_begin, h_end) &&
456               get_keyword_value(tmp_string, "RPATH", r_begin, r_end) &&
457               get_keyword_value(tmp_string, "LPATH", l_begin, l_end)))
458         {
459                 return (_Tt_string)0;
460         }
461
462
463         // get the raw data in the canonical_path, and parse out
464         // the components
465
466         _canonical_path = canonical_name;
467
468 #ifdef notdef
469         printf("DEBUG parse_netfilename: _canonical_path == %s\n", (char *) _canonical_path);
470 #endif
471
472         tmp_string = _canonical_path.split(':', dummy);
473
474         _hostname = tmp_string.mid(h_begin, h_end - h_begin + 1);
475
476         _rpath = tmp_string.mid(r_begin, r_end - r_begin + 1);
477
478 #ifdef notdef
479         printf("DEBUG parse_netfilename: _rpath == %s\n", (char *) _rpath);
480 #endif
481
482         _lpath = tmp_string.mid(l_begin, l_end - l_begin + 1);
483
484 #ifdef notdef
485         printf("DEBUG parse_netfilename: _lpath == %s\n", (char *) _lpath);
486 #endif
487
488
489         return _canonical_path;
490 }
491
492
493 _Tt_string _Tt_api_filename_map::
494 canonical_path_val()
495 {
496         return _canonical_path;
497 }
498
499 _Tt_string _Tt_api_filename_map::
500 lpath_val()
501 {
502         return _lpath;
503 }
504
505 _Tt_string _Tt_api_filename_map::
506 rpath_val()
507 {
508         return _rpath;
509 }
510
511 _Tt_string _Tt_api_filename_map::
512 hostname_val()
513 {
514         return _hostname;
515 }
516
517
518 // Find a keyword in the netfile prefix and return the associated start and
519 // end byte offsets.  If the keyword is not found, or the string is badly
520 // formed, return 0. Else, if the parsing succeeds, return 1.
521 // The TT_NETFILE_PREFIX  and everything from the colon on are assumed
522 // to already be lopped off s.
523
524 static int
525 get_keyword_value(_Tt_string s, const char *keyword, int &start, int &end)
526 {
527         // It makes the parsing more uniform if we can always expect to
528         // find the keyword preceded by a comma, which is true for all
529         // but the first keyword.  So put a comma on the front, and it
530         // will be true for all keywords.  Similarly, put a comma
531         // in front of the keyword and an equals sign after it.
532         // Similarly, add a comma at the end of s so even the last
533         // keyword is terminated by one.
534         
535         _Tt_string ts = ",";
536         _Tt_string tk = ",";
537         _Tt_string junk;
538         int i;
539
540         ts = ts.cat(s).cat(",");
541         tk = tk.cat(keyword).cat("=");
542         
543         if (-1==(i=ts.index(tk))) {
544                 return 0;
545         }
546         // lop off extraneous stuff before the keyword
547         ts = ts.mid(i,ts.len()-i);
548         // lop off the keyword
549         ts = ts.split('=',junk);
550         // lop off extraneous stuff after the comma that terminates
551         // this keyword
552         ts.split(',',junk);
553         i = sscanf((char *)ts, "%d-%d", &start, &end);
554         return (i==2);
555 }