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