8f1490c528df835fd33c3e22b1e8ba5e3a886266
[samba.git] / source / smbd / mangle.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Name mangling
5    Copyright (C) Andrew Tridgell 1992-1995
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "loadparm.h"
24
25 extern int DEBUGLEVEL;
26 extern int case_default;
27 extern BOOL case_mangle;
28
29 /****************************************************************************
30 provide a checksum on a string
31 ****************************************************************************/
32 int str_checksum(char *s)
33 {
34   int res = 0;
35   int c;
36   int i=0;
37   while (*s)
38     {
39       c = *s;
40       res ^= (c << (i % 15)) ^ (c >> (15-(i%15)));
41       s++; i++;
42     }
43   return(res);
44 }
45
46 /****************************************************************************
47 return True if a name is a special msdos reserved name
48 ****************************************************************************/
49 static BOOL is_reserved_msdos(char *fname)
50 {
51   char upperFname[13];
52   char *p;
53
54   StrnCpy (upperFname, fname, 12);
55
56   /* lpt1.txt and con.txt etc are also illegal */
57   p=strchr(upperFname,'.');
58   if (p)
59    *p='\0';
60   strupper (upperFname);
61   if ((strcmp(upperFname,"CLOCK$") == 0) ||
62     (strcmp(upperFname,"CON") == 0) ||
63     (strcmp(upperFname,"AUX") == 0) ||
64     (strcmp(upperFname,"COM1") == 0) ||
65     (strcmp(upperFname,"COM2") == 0) ||
66     (strcmp(upperFname,"COM3") == 0) ||
67     (strcmp(upperFname,"COM4") == 0) ||
68     (strcmp(upperFname,"LPT1") == 0) ||
69     (strcmp(upperFname,"LPT2") == 0) ||
70     (strcmp(upperFname,"LPT3") == 0) ||
71     (strcmp(upperFname,"NUL") == 0) ||
72     (strcmp(upperFname,"PRN") == 0))
73       return (True) ;
74
75   return (False);
76 }
77
78
79
80 /****************************************************************************
81 return True if a name is in 8.3 dos format
82 ****************************************************************************/
83 BOOL is_8_3(char *fname)
84 {
85   int len;
86   char *dot_pos;
87   char *slash_pos = strrchr(fname,'/');
88   int l;
89
90   if (slash_pos) fname = slash_pos+1;
91   len = strlen(fname);
92
93   dot_pos = strchr(fname,'.');
94
95   DEBUG(5,("checking %s for 8.3\n",fname));
96
97   if (case_mangle)
98     switch (case_default)
99       {
100       case CASE_LOWER:
101         if (strhasupper(fname)) return(False);
102         break;
103       case CASE_UPPER:
104         if (strhaslower(fname)) return(False);
105         break;
106       }
107
108   /* can't be longer than 12 chars */
109   if (len == 0 || len > 12)
110     return(False);
111
112   /* can't be an MS-DOS Special file such as lpt1 or even lpt1.txt */
113   if (is_reserved_msdos(fname))
114     return(False);
115
116   /* can't contain invalid dos chars */
117   /* Windows use the ANSI charset.
118      But filenames are translated in the PC charset.
119      This Translation may be more or less relaxed depending
120      the Windows application. */
121
122   /* %%% A nice improvment to name mangling would be to translate
123      filename to ANSI charset on the smb server host */
124
125   {
126     char *p = fname;
127 #ifdef KANJI
128     dot_pos = 0;
129     while (*p)
130       {
131           if (is_shift_jis (*p)) {
132               p += 2;
133           } else if (is_kana (*p)) {
134               p ++;
135           } else {
136               if (*p == '.' && !dot_pos)
137                   dot_pos = (char *) p;
138               if (!isdoschar(*p))
139                   return(False);
140               p++;
141           }
142       }      
143 #else
144     while (*p)
145       {
146         if (!isdoschar(*p))
147           return(False);
148         p++;
149       }      
150 #endif /* KANJI */
151   }
152
153   /* no dot and less than 9 means OK */
154   if (!dot_pos)
155     return(len <= 8);
156         
157   l = PTR_DIFF(dot_pos,fname);
158
159   /* base must be at least 1 char except special cases . and .. */
160   if (l == 0)
161     return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0);
162
163   /* base can't be greater than 8 */
164   if (l > 8)
165     return(False);
166
167   if (lp_strip_dot() && 
168       len - l == 1 &&
169       !strchr(dot_pos+1,'.'))
170     {
171       *dot_pos = 0;
172       return(True);
173     }
174
175   /* extension must be between 1 and 3 */
176   if ( (len - l < 2 ) || (len - l > 4) )
177     return(False);
178
179   /* extension can't have a dot */
180   if (strchr(dot_pos+1,'.'))
181     return(False);
182
183   /* must be in 8.3 format */
184   return(True);
185 }
186
187
188
189 /*
190 keep a stack of name mangling results - just
191 so file moves and copies have a chance of working
192 */
193 fstring *mangled_stack = NULL;
194 int mangled_stack_size = 0;
195 int mangled_stack_len = 0;
196
197 /****************************************************************************
198 create the mangled stack
199 ****************************************************************************/
200 void create_mangled_stack(int size)
201 {
202   if (mangled_stack)
203     {
204       free(mangled_stack);
205       mangled_stack_size = 0;
206       mangled_stack_len = 0;
207     }
208   if (size > 0)
209     mangled_stack = (fstring *)malloc(sizeof(fstring)*size);
210   if (mangled_stack) mangled_stack_size = size;
211 }
212
213 /****************************************************************************
214 push a mangled name onto the stack
215 ****************************************************************************/
216 static void push_mangled_name(char *s)
217 {
218   int i;
219   char *p;
220
221   if (!mangled_stack)
222     return;
223
224   for (i=0;i<mangled_stack_len;i++)
225     if (strcmp(s,mangled_stack[i]) == 0)
226       {
227         array_promote(mangled_stack[0],sizeof(fstring),i);      
228         return;
229       }
230
231   memmove(mangled_stack[1],mangled_stack[0],
232           sizeof(fstring)*MIN(mangled_stack_len,mangled_stack_size-1));
233   strcpy(mangled_stack[0],s);
234   p = strrchr(mangled_stack[0],'.');
235   if (p && (!strhasupper(p+1)) && (strlen(p+1) < 4))
236     *p = 0;
237   mangled_stack_len = MIN(mangled_stack_size,mangled_stack_len+1);
238 }
239
240 /****************************************************************************
241 check for a name on the mangled name stack
242 ****************************************************************************/
243 BOOL check_mangled_stack(char *s)
244 {
245   int i;
246   pstring tmpname;
247   char extension[5];
248   char *p = strrchr(s,'.');
249   BOOL check_extension = False;
250
251   extension[0] = 0;
252
253   if (!mangled_stack) return(False);
254
255   if (p)
256     {
257       check_extension = True;
258       StrnCpy(extension,p,4);
259       strlower(extension); /* XXXXXXX */
260     }
261
262   for (i=0;i<mangled_stack_len;i++)
263     {
264       strcpy(tmpname,mangled_stack[i]);
265       mangle_name_83(tmpname);
266       if (strequal(tmpname,s))
267         {
268           strcpy(s,mangled_stack[i]);
269           break;
270         }
271       if (check_extension && !strchr(mangled_stack[i],'.'))
272         {
273           strcpy(tmpname,mangled_stack[i]);
274           strcat(tmpname,extension);
275           mangle_name_83(tmpname);
276           if (strequal(tmpname,s))
277             {
278               strcpy(s,mangled_stack[i]);
279               strcat(s,extension);
280               break;
281             }     
282         }
283     }
284
285   if (i < mangled_stack_len)
286     {
287       DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i]));
288       array_promote(mangled_stack[0],sizeof(fstring),i);
289       return(True);      
290     }
291
292   return(False);
293 }       
294
295 static char *map_filename(char *s, /* This is null terminated */
296                           char *pattern, /* This isn't. */
297                           int len) /* This is the length of pattern. */
298 {
299   static pstring matching_bit;  /* The bit of the string which matches */
300                                 /* a * in pattern if indeed there is a * */
301   char *sp;                     /* Pointer into s. */
302   char *pp;                     /* Pointer into p. */
303   char *match_start;            /* Where the matching bit starts. */
304   pstring pat;
305
306   StrnCpy(pat, pattern, len);   /* Get pattern into a proper string! */
307   strcpy(matching_bit,"");      /* Match but no star gets this. */
308   pp = pat;                     /* Initialise the pointers. */
309   sp = s;
310   if ((len == 1) && (*pattern == '*')) {
311     return NULL;                /* Impossible, too ambiguous for */
312                                 /* words! */
313   }
314
315   while ((*sp)                  /* Not the end of the string. */
316          && (*pp)               /* Not the end of the pattern. */
317          && (*sp == *pp)        /* The two match. */
318          && (*pp != '*')) {     /* No wildcard. */
319     sp++;                       /* Keep looking. */
320     pp++;
321   }
322   if (!*sp && !*pp)             /* End of pattern. */
323     return matching_bit;        /* Simple match.  Return empty string. */
324   if (*pp == '*') {
325     pp++;                       /* Always interrested in the chacter */
326                                 /* after the '*' */
327     if (!*pp) {                 /* It is at the end of the pattern. */
328       StrnCpy(matching_bit, s, sp-s);
329       return matching_bit;
330     } else {
331       /* The next character in pattern must match a character further */
332       /* along s than sp so look for that character. */
333       match_start = sp;
334       while ((*sp)              /* Not the end of s. */
335              && (*sp != *pp))   /* Not the same  */
336         sp++;                   /* Keep looking. */
337       if (!*sp) {               /* Got to the end without a match. */
338         return NULL;
339       } else {                  /* Still hope for a match. */
340         /* Now sp should point to a matching character. */
341         StrnCpy(matching_bit, match_start, sp-match_start);
342         /* Back to needing a stright match again. */
343         while ((*sp)            /* Not the end of the string. */
344                && (*pp)         /* Not the end of the pattern. */
345                && (*sp == *pp)) { /* The two match. */
346           sp++;                 /* Keep looking. */
347           pp++;
348         }
349         if (!*sp && !*pp)       /* Both at end so it matched */
350           return matching_bit;
351         else
352           return NULL;
353       }
354     }
355   }
356   return NULL;                  /* No match. */
357 }
358
359
360 /* this is the magic char used for mangling */
361 char magic_char = '~';
362
363
364 /****************************************************************************
365 determine whther is name could be a mangled name
366 ****************************************************************************/
367 BOOL is_mangled(char *s)
368 {
369   char *m = strchr(s,magic_char);
370   if (!m) return(False);
371
372   /* we use two base 36 chars efore the extension */
373   if (m[1] == '.' || m[1] == 0 ||
374       m[2] == '.' || m[2] == 0 ||
375       (m[3] != '.' && m[3] != 0))
376     return(is_mangled(m+1));
377
378   /* it could be */
379   return(True);
380 }
381
382
383
384 /****************************************************************************
385 return a base 36 character. v must be from 0 to 35.
386 ****************************************************************************/
387 static char base36(int v)
388 {
389   v = v % 36;
390   if (v < 10)
391     return('0'+v);
392   else /* needed to work around a DEC C compiler bug */
393     return('A' + (v-10));
394 }
395
396
397 static void do_fwd_mangled_map(char *s, char *MangledMap)
398 {
399   /* MangledMap is a series of name pairs in () separated by spaces.
400    * If s matches the first of the pair then the name given is the
401    * second of the pair.  A * means any number of any character and if
402    * present in the second of the pair as well as the first the
403    * matching part of the first string takes the place of the * in the
404    * second.
405    *
406    * I wanted this so that we could have RCS files which can be used
407    * by UNIX and DOS programs.  My mapping string is (RCS rcs) which
408    * converts the UNIX RCS file subdirectory to lowercase thus
409    * preventing mangling.
410    */
411   char *start=MangledMap;       /* Use this to search for mappings. */
412   char *end;                    /* Used to find the end of strings. */
413   char *match_string;
414   pstring new_string;           /* Make up the result here. */
415   char *np;                     /* Points into new_string. */
416
417   DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap));
418   while (*start) {
419     while ((*start) && (*start != '('))
420       start++;
421     start++;                    /* Skip the ( */
422     if (!*start)
423       continue;                 /* Always check for the end. */
424     end = start;                /* Search for the ' ' or a ')' */
425     DEBUG(5,("Start of first in pair '%s'\n", start));
426     while ((*end) && !((*end == ' ') || (*end == ')')))
427       end++;
428     if (!*end) {
429       start = end;
430       continue;                 /* Always check for the end. */
431     }
432     DEBUG(5,("End of first in pair '%s'\n", end));
433     if ((match_string = map_filename(s, start, end-start))) {
434       DEBUG(5,("Found a match\n"));
435       /* Found a match. */
436       start = end+1;            /* Point to start of what it is to become. */
437       DEBUG(5,("Start of second in pair '%s'\n", start));
438       end = start;
439       np = new_string;
440       while ((*end)             /* Not the end of string. */
441              && (*end != ')')   /* Not the end of the pattern. */
442              && (*end != '*'))  /* Not a wildcard. */
443         *np++ = *end++;
444       if (!*end) {
445         start = end;
446         continue;               /* Always check for the end. */
447       }
448       if (*end == '*') {
449         strcpy(np, match_string);
450         np += strlen(match_string);
451         end++;                  /* Skip the '*' */
452         while ((*end)             /* Not the end of string. */
453                && (*end != ')')   /* Not the end of the pattern. */
454                && (*end != '*'))  /* Not a wildcard. */
455           *np++ = *end++;
456       }
457       if (!*end) {
458         start = end;
459         continue;               /* Always check for the end. */
460       }
461       *np++ = '\0';             /* NULL terminate it. */
462       DEBUG(5,("End of second in pair '%s'\n", end));
463       strcpy(s, new_string);    /* Substitute with the new name. */
464       DEBUG(5,("s is now '%s'\n", s));
465     }
466     start = end;              /* Skip a bit which cannot be wanted */
467     /* anymore. */
468     start++;
469   }
470 }
471
472 /****************************************************************************
473 do the actual mangling to 8.3 format
474 ****************************************************************************/
475 void mangle_name_83(char *s)
476 {
477   int csum = str_checksum(s);
478   char *p;
479   char extension[4];
480   char base[9];
481   int baselen = 0;
482   int extlen = 0;
483
484   extension[0]=0;
485   base[0]=0;
486
487   p = strrchr(s,'.');  
488   if (p && (strlen(p+1)<4) )
489     {
490       BOOL all_normal = (strisnormal(p+1)); /* XXXXXXXXX */
491       if (all_normal && p[1] != 0)
492         {
493           *p = 0;
494           csum = str_checksum(s);
495           *p = '.';
496         }
497     }
498       
499
500   strupper(s);
501
502   DEBUG(5,("Mangling name %s to ",s));
503
504   if (p)
505     {
506       if (p == s)
507         strcpy(extension,"___");
508       else
509         {
510           *p++ = 0;
511           while (*p && extlen < 3)
512             {
513               if (isdoschar(*p) && *p != '.')
514                 extension[extlen++] = *p;
515               p++;
516             }
517           extension[extlen] = 0;
518         }
519     }
520
521   p = s;
522
523   while (*p && baselen < 5)
524     {
525       if (isdoschar(*p) && *p != '.')
526         base[baselen++] = *p;
527       p++;
528     }
529   base[baselen] = 0;
530
531   csum = csum % (36*36);
532
533     sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36));
534
535   if (*extension)
536     {
537       strcat(s,".");
538       strcat(s,extension);
539     }
540   DEBUG(5,("%s\n",s));
541 }
542
543
544
545 /*******************************************************************
546   work out if a name is illegal, even for long names
547   ******************************************************************/
548 static BOOL illegal_name(char *name)
549 {
550   static unsigned char illegal[256];
551   static BOOL initialised=False;
552   unsigned char *s;
553
554   if (!initialised) {
555     char *ill = "*\\/?<>|\":{}";
556     initialised = True;
557   
558     bzero((char *)illegal,256);
559     for (s = (unsigned char *)ill; *s; s++)
560       illegal[*s] = True;
561   }
562
563 #ifdef KANJI
564   for (s = (unsigned char *)name; *s;) {
565     if (is_shift_jis (*s)) {
566       s += 2;
567     } else if (illegal[*s]) {
568       return(True);
569     } else {
570       s++;
571     }
572   }
573 #else
574   for (s = (unsigned char *)name;*s;s++)
575     if (illegal[*s]) return(True);
576 #endif
577
578
579   return(False);
580 }
581
582
583 /****************************************************************************
584 convert a filename to DOS format. return True if successful.
585 ****************************************************************************/
586 BOOL name_map_mangle(char *OutName,BOOL need83,int snum)
587 {
588 #ifdef MANGLE_LONG_FILENAMES
589   if (!need83 && illegal_name(OutName)) need83 = True;
590 #endif  
591
592   /* apply any name mappings */
593   {
594     char *map = lp_mangled_map(snum);
595     if (map && *map)
596       do_fwd_mangled_map(OutName,map);
597   }
598
599   /* check if it's already in 8.3 format */
600   if (need83 && !is_8_3(OutName)) {
601     if (!lp_manglednames(snum)) return(False);
602
603     /* mangle it into 8.3 */
604     push_mangled_name(OutName);  
605     mangle_name_83(OutName);
606   }
607   
608   return(True);
609 }
610