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