This is *not* a big change (although it looks like one).
[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-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 << (i % 15)) ^ (c >> (15-(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
136     if(lp_client_code_page() == KANJI_CODEPAGE)
137       {
138       dot_pos = 0;
139       while (*p)
140         {
141         if (is_shift_jis (*p)) 
142           p += 2;
143         else if (is_kana (*p)) 
144           p ++;
145         else 
146           {
147           if (*p == '.' && !dot_pos)
148             dot_pos = (char *) p;
149           if (!isdoschar(*p))
150             return(False);
151           p++;
152           }
153         }
154       }      
155     else
156       {
157       while (*p)
158         {
159         if (!isdoschar(*p))
160           return(False);
161         p++;
162         }      
163       }
164     }
165
166   /* no dot and less than 9 means OK */
167   if (!dot_pos)
168     return(len <= 8);
169         
170   l = PTR_DIFF(dot_pos,fname);
171
172   /* base must be at least 1 char except special cases . and .. */
173   if( l == 0 )
174     return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0);
175
176   /* base can't be greater than 8 */
177   if( l > 8 )
178     return(False);
179
180   if( lp_strip_dot() && 
181       len - l == 1 &&
182       !strchr(dot_pos+1,'.') )
183     {
184     *dot_pos = 0;
185     return(True);
186     }
187
188   /* extension must be between 1 and 3 */
189   if( (len - l < 2 ) || (len - l > 4) )
190     return(False);
191
192   /* extension can't have a dot */
193   if( strchr(dot_pos+1,'.') )
194     return(False);
195
196   /* must be in 8.3 format */
197   return(True);
198   } /* is_8_3 */
199
200 /* -------------------------------------------------------------------------- **
201  * This section creates and maintains a stack of name mangling results.
202  * The original comments read: "keep a stack of name mangling results - just
203  * so file moves and copies have a chance of working" (whatever that means).
204  *
205  * There are three functions to manage the stack:
206  *   reset_mangled_stack() -
207  *   push_mangled_name()    -
208  *   check_mangled_stack()  -
209  */
210
211 fstring *mangled_stack = NULL;
212 int mangled_stack_size = 0;
213 int mangled_stack_len = 0;
214
215 /****************************************************************************
216  * create the mangled stack CRH
217  ****************************************************************************/
218 void reset_mangled_stack( int size )
219   {
220   if( mangled_stack )
221     {
222     free(mangled_stack);
223     mangled_stack_size = 0;
224     mangled_stack_len = 0;
225     }
226
227   if( size > 0 )
228     {
229     mangled_stack = (fstring *)malloc( sizeof(fstring) * size );
230     if( mangled_stack )
231       mangled_stack_size = size;
232     }
233   else
234     mangled_stack = NULL;
235   } /* create_mangled_stack */
236
237 /****************************************************************************
238  * push a mangled name onto the stack CRH
239  ****************************************************************************/
240 static void push_mangled_name(char *s)
241   {
242   int i;
243   char *p;
244
245   /* If the stack doesn't exist... Fail. */
246   if( !mangled_stack )
247     return;
248
249   /* If name <s> is already on the stack, move it to the top. */
250   for( i=0; i<mangled_stack_len; i++ )
251     {
252     if( strcmp( s, mangled_stack[i] ) == 0 )
253       {
254       array_promote( mangled_stack[0],sizeof(fstring), i );
255       return;
256       }
257     }
258
259   /* If name <s> wasn't already there, add it to the top of the stack. */
260   memmove( mangled_stack[1], mangled_stack[0],
261            sizeof(fstring) * MIN(mangled_stack_len, mangled_stack_size-1) );
262   strcpy( mangled_stack[0], s );
263   mangled_stack_len = MIN( mangled_stack_size, mangled_stack_len+1 );
264
265   /* Hmmm...
266    *  Find the last dot '.' in the name,
267    *  if there are any upper case characters past the last dot
268    *  and there are no more than three characters past the last dot
269    *  then terminate the name *at* the last dot.
270    */
271   p = strrchr( mangled_stack[0], '.' );
272   if( p && (!strhasupper(p+1)) && (strlen(p+1) < (size_t)4) )
273     *p = 0;
274
275   } /* push_mangled_name */
276
277 /****************************************************************************
278  * check for a name on the mangled name stack CRH
279  ****************************************************************************/
280 BOOL check_mangled_stack(char *s)
281   {
282   int i;
283   pstring tmpname;
284   char extension[5];
285   char *p              = strrchr( s, '.' );
286   BOOL check_extension = False;
287
288   extension[0] = 0;
289
290   /* If the stack doesn't exist, fail. */
291   if( !mangled_stack )
292     return(False);
293
294   /* If there is a file extension, then we need to play with it, too. */
295   if( p )
296     {
297     check_extension = True;
298     StrnCpy( extension, p, 4 );
299     strlower( extension ); /* XXXXXXX */
300     }
301
302   for( i=0; i<mangled_stack_len; i++ )
303     {
304     strcpy(tmpname,mangled_stack[i]);
305     mangle_name_83(tmpname);
306     if( strequal(tmpname,s) )
307       {
308       strcpy(s,mangled_stack[i]);
309       break;
310       }
311     if( check_extension && !strchr(mangled_stack[i],'.') )
312       {
313       pstrcpy(tmpname,mangled_stack[i]);
314       strcat(tmpname,extension);
315       mangle_name_83(tmpname);
316       if( strequal(tmpname,s) )
317         {
318         strcpy(s,mangled_stack[i]);
319         strcat(s,extension);
320         break;
321         }          
322       }
323     }
324
325   if( i < mangled_stack_len )
326     {
327     DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i]));
328     array_promote(mangled_stack[0],sizeof(fstring),i);
329     return(True);      
330     }
331
332   return(False);
333   } /* check_mangled_stack */
334
335
336 /* End of the mangled stack section.
337  * -------------------------------------------------------------------------- **
338  */
339
340
341 static char *map_filename( char *s,         /* This is null terminated */
342                            char *pattern,   /* This isn't. */
343                            int len )        /* This is the length of pattern. */
344   {
345   static pstring matching_bit;  /* The bit of the string which matches */
346                                 /* a * in pattern if indeed there is a * */
347   char *sp;                     /* Pointer into s. */
348   char *pp;                     /* Pointer into p. */
349   char *match_start;            /* Where the matching bit starts. */
350   pstring pat;
351
352   StrnCpy(pat, pattern, len);   /* Get pattern into a proper string! */
353   pstrcpy(matching_bit,"");     /* Match but no star gets this. */
354   pp = pat;                     /* Initialise the pointers. */
355   sp = s;
356   if( (len == 1) && (*pattern == '*') )
357     {
358     return NULL;                /* Impossible, too ambiguous for */
359     }                           /* words! */
360
361   while ((*sp)                  /* Not the end of the string. */
362          && (*pp)               /* Not the end of the pattern. */
363          && (*sp == *pp)        /* The two match. */
364          && (*pp != '*'))       /* No wildcard. */
365     {
366     sp++;                       /* Keep looking. */
367     pp++;
368     }
369
370   if( !*sp && !*pp )            /* End of pattern. */
371     return( matching_bit );     /* Simple match.  Return empty string. */
372
373   if (*pp == '*')
374     {
375     pp++;                       /* Always interrested in the chacter */
376                                 /* after the '*' */
377     if (!*pp)                   /* It is at the end of the pattern. */
378       {
379       StrnCpy(matching_bit, s, sp-s);
380       return matching_bit;
381       }
382     else
383       {
384       /* The next character in pattern must match a character further */
385       /* along s than sp so look for that character. */
386       match_start = sp;
387       while( (*sp)              /* Not the end of s. */
388              && (*sp != *pp))   /* Not the same  */
389         sp++;                   /* Keep looking. */
390       if (!*sp)                 /* Got to the end without a match. */
391         {
392         return NULL;
393         }                       /* Still hope for a match. */
394       else
395         {
396         /* Now sp should point to a matching character. */
397         StrnCpy(matching_bit, match_start, sp-match_start);
398         /* Back to needing a stright match again. */
399         while( (*sp)            /* Not the end of the string. */
400                && (*pp)         /* Not the end of the pattern. */
401                && (*sp == *pp) ) /* The two match. */
402           {
403           sp++;                 /* Keep looking. */
404           pp++;
405           }
406         if (!*sp && !*pp)       /* Both at end so it matched */
407           return matching_bit;
408         else
409           return NULL;
410         }
411       }
412     }
413   return NULL;                  /* No match. */
414   } /* map_filename */
415
416
417 /* this is the magic char used for mangling */
418 char magic_char = '~';
419
420
421 /****************************************************************************
422 return True if the name could be a mangled name
423 ****************************************************************************/
424 BOOL is_mangled( char *s )
425   {
426   char *m = strchr(s,magic_char);
427
428   if( !m )
429     return(False);
430
431   /* we use two base 36 chars before the extension */
432   if( m[1] == '.' || m[1] == 0 ||
433       m[2] == '.' || m[2] == 0 ||
434       (m[3] != '.' && m[3] != 0) )
435     return( is_mangled(m+1) );
436
437   /* it could be */
438   return(True);
439   } /* is_mangled */
440
441
442
443 /****************************************************************************
444 return a base 36 character. v must be from 0 to 35.
445 ****************************************************************************/
446 static char base36(unsigned int v)
447   {
448   static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
449   return basechars[v % 36];
450   } /* base36 */
451
452
453 static void do_fwd_mangled_map(char *s, char *MangledMap)
454   {
455   /* MangledMap is a series of name pairs in () separated by spaces.
456    * If s matches the first of the pair then the name given is the
457    * second of the pair.  A * means any number of any character and if
458    * present in the second of the pair as well as the first the
459    * matching part of the first string takes the place of the * in the
460    * second.
461    *
462    * I wanted this so that we could have RCS files which can be used
463    * by UNIX and DOS programs.  My mapping string is (RCS rcs) which
464    * converts the UNIX RCS file subdirectory to lowercase thus
465    * preventing mangling.
466    */
467   char *start=MangledMap;       /* Use this to search for mappings. */
468   char *end;                    /* Used to find the end of strings. */
469   char *match_string;
470   pstring new_string;           /* Make up the result here. */
471   char *np;                     /* Points into new_string. */
472
473   DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap));
474   while (*start)
475     {
476     while ((*start) && (*start != '('))
477       start++;
478     if (!*start)
479       continue;                 /* Always check for the end. */
480     start++;                    /* Skip the ( */
481     end = start;                /* Search for the ' ' or a ')' */
482     DEBUG(5,("Start of first in pair '%s'\n", start));
483     while ((*end) && !((*end == ' ') || (*end == ')')))
484       end++;
485     if (!*end)
486       {
487       start = end;
488       continue;                 /* Always check for the end. */
489       }
490     DEBUG(5,("End of first in pair '%s'\n", end));
491     if ((match_string = map_filename(s, start, end-start)))
492       {
493       DEBUG(5,("Found a match\n"));
494       /* Found a match. */
495       start = end+1;            /* Point to start of what it is to become. */
496       DEBUG(5,("Start of second in pair '%s'\n", start));
497       end = start;
498       np = new_string;
499       while ((*end)             /* Not the end of string. */
500              && (*end != ')')   /* Not the end of the pattern. */
501              && (*end != '*'))  /* Not a wildcard. */
502         *np++ = *end++;
503       if (!*end)
504         {
505         start = end;
506         continue;               /* Always check for the end. */
507         }
508       if (*end == '*')
509         {
510         pstrcpy(np, match_string);
511         np += strlen(match_string);
512         end++;                  /* Skip the '*' */
513         while ((*end)             /* Not the end of string. */
514                && (*end != ')')   /* Not the end of the pattern. */
515                && (*end != '*'))  /* Not a wildcard. */
516           *np++ = *end++;
517         }
518       if (!*end)
519         {
520         start = end;
521         continue;               /* Always check for the end. */
522         }
523       *np++ = '\0';             /* NULL terminate it. */
524       DEBUG(5,("End of second in pair '%s'\n", end));
525       pstrcpy(s, new_string);    /* Substitute with the new name. */
526       DEBUG(5,("s is now '%s'\n", s));
527       }
528     start = end;              /* Skip a bit which cannot be wanted */
529     /* anymore. */
530     start++;
531     }
532   } /* do_fwd_mangled_map */
533
534 /****************************************************************************
535 do the actual mangling to 8.3 format
536 ****************************************************************************/
537 void mangle_name_83(char *s)
538   {
539   int csum = str_checksum(s);
540   char *p;
541   char extension[4];
542   char base[9];
543   int baselen = 0;
544   int extlen = 0;
545
546   extension[0]=0;
547   base[0]=0;
548
549   p = strrchr(s,'.');  
550   if( p && (strlen(p+1) < (size_t)4) )
551     {
552     BOOL all_normal = (strisnormal(p+1)); /* XXXXXXXXX */
553
554     if (all_normal && p[1] != 0)
555       {
556       *p = 0;
557       csum = str_checksum(s);
558         *p = '.';
559       }
560     }
561
562   strupper(s);
563
564   DEBUG(5,("Mangling name %s to ",s));
565
566   if( p )
567     {
568     if (p == s)
569       strcpy(extension,"___");
570     else
571       {
572       *p++ = 0;
573       while (*p && extlen < 3)
574         {
575         if(lp_client_code_page() == KANJI_CODEPAGE)
576           {
577           if (is_shift_jis (*p))
578             {
579             if (extlen < 2)
580               {
581               extension[extlen++] = p[0];
582               extension[extlen++] = p[1];
583               }
584             else 
585               {
586               extension[extlen++] = base36 (((unsigned char) *p) % 36);
587               }
588             p += 2;
589             }
590           else
591             {
592             if( is_kana (*p) )
593               {
594               extension[extlen++] = p[0];
595               p++;
596               }
597             else 
598               {
599               if (isdoschar (*p) && *p != '.')
600                 extension[extlen++] = p[0];
601               p++;
602               }
603             }
604           }
605         else
606           {
607           if (isdoschar(*p) && *p != '.')
608             extension[extlen++] = *p;
609           p++;
610           }
611         }
612       extension[extlen] = 0;
613       }
614     }
615
616   p = s;
617
618   while (*p && baselen < 5)
619     {
620     if(lp_client_code_page() == KANJI_CODEPAGE)
621       {
622       if (is_shift_jis (*p))
623         {
624         if (baselen < 4)
625           {
626           base[baselen++] = p[0];
627           base[baselen++] = p[1];
628           }
629         else 
630           {
631           base[baselen++] = base36 (((unsigned char) *p) % 36);
632           }
633         p += 2;
634         }
635       else
636         {
637         if( is_kana (*p) )
638           {
639           base[baselen++] = p[0];
640           p++;
641           }
642         else 
643           {
644           if (isdoschar (*p) && *p != '.')
645             base[baselen++] = p[0];
646           p++;
647           }
648         }
649       }
650     else
651       {
652       if (isdoschar(*p) && *p != '.')
653         base[baselen++] = *p;
654       p++;
655       }
656     }
657   base[baselen] = 0;
658
659   csum = csum % (36*36);
660
661   sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36));
662
663   if( *extension )
664     {
665     strcat(s,".");
666     strcat(s,extension);
667     }
668   DEBUG(5,("%s\n",s));
669
670   } /* mangle_name_83 */
671
672
673
674 /*******************************************************************
675   work out if a name is illegal, even for long names
676   ******************************************************************/
677 static BOOL illegal_name(char *name)
678   {
679   static unsigned char illegal[256];
680   static BOOL initialised=False;
681   unsigned char *s;
682
683   if( !initialised )
684     {
685     char *ill = "*\\/?<>|\":";
686     initialised = True;
687   
688     bzero((char *)illegal,256);
689     for( s = (unsigned char *)ill; *s; s++ )
690       illegal[*s] = True;
691     }
692
693   if(lp_client_code_page() == KANJI_CODEPAGE)
694     {
695     for (s = (unsigned char *)name; *s;)
696       {
697       if (is_shift_jis (*s))
698         s += 2;
699       else
700         {
701         if (illegal[*s])
702           return(True);
703         else
704           s++;
705         }
706       }
707     }
708   else
709     {
710     for (s = (unsigned char *)name;*s;s++)
711       if (illegal[*s]) return(True);
712     }
713
714   return(False);
715   } /* illegal_name */
716
717
718 /****************************************************************************
719 convert a filename to DOS format. return True if successful.
720 ****************************************************************************/
721 BOOL name_map_mangle(char *OutName,BOOL need83,int snum)
722   {
723 #ifdef MANGLE_LONG_FILENAMES
724   if( !need83 && illegal_name(OutName) )
725     need83 = True;
726 #endif  
727
728   /* apply any name mappings */
729   {
730   char *map = lp_mangled_map(snum);
731
732   if (map && *map)
733     do_fwd_mangled_map(OutName,map);
734   }
735
736   /* check if it's already in 8.3 format */
737   if( need83 && !is_8_3(OutName, True) )
738     {
739     if( !lp_manglednames(snum) )
740       return(False);
741
742     /* mangle it into 8.3 */
743     push_mangled_name(OutName);  
744     mangle_name_83(OutName);
745     }
746   
747   return(True);
748   } /* name_map_mangle */