charcnv.c client.c clitar.c kanji.c kanji.h loadparm.c
[nivanova/samba-autobuild/.git] / source3 / smbd / mangle.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Name mangling
5    Copyright (C) Andrew Tridgell 1992-1997
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     if(lp_client_code_page() == KANJI_CODEPAGE)
127     {
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         {
137           if (*p == '.' && !dot_pos)
138             dot_pos = (char *) p;
139           if (!isdoschar(*p))
140             return(False);
141           p++;
142         }
143       }
144     }      
145     else
146     {
147       while (*p)
148       {
149         if (!isdoschar(*p))
150           return(False);
151         p++;
152       }      
153     }
154   }
155
156   /* no dot and less than 9 means OK */
157   if (!dot_pos)
158     return(len <= 8);
159         
160   l = PTR_DIFF(dot_pos,fname);
161
162   /* base must be at least 1 char except special cases . and .. */
163   if (l == 0)
164     return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0);
165
166   /* base can't be greater than 8 */
167   if (l > 8)
168     return(False);
169
170   if (lp_strip_dot() && 
171       len - l == 1 &&
172       !strchr(dot_pos+1,'.'))
173     {
174       *dot_pos = 0;
175       return(True);
176     }
177
178   /* extension must be between 1 and 3 */
179   if ( (len - l < 2 ) || (len - l > 4) )
180     return(False);
181
182   /* extension can't have a dot */
183   if (strchr(dot_pos+1,'.'))
184     return(False);
185
186   /* must be in 8.3 format */
187   return(True);
188 }
189
190
191
192 /*
193 keep a stack of name mangling results - just
194 so file moves and copies have a chance of working
195 */
196 fstring *mangled_stack = NULL;
197 int mangled_stack_size = 0;
198 int mangled_stack_len = 0;
199
200 /****************************************************************************
201 create the mangled stack
202 ****************************************************************************/
203 void create_mangled_stack(int size)
204 {
205   if (mangled_stack)
206     {
207       free(mangled_stack);
208       mangled_stack_size = 0;
209       mangled_stack_len = 0;
210     }
211   if (size > 0)
212     mangled_stack = (fstring *)malloc(sizeof(fstring)*size);
213   if (mangled_stack) mangled_stack_size = size;
214 }
215
216 /****************************************************************************
217 push a mangled name onto the stack
218 ****************************************************************************/
219 static void push_mangled_name(char *s)
220 {
221   int i;
222   char *p;
223
224   if (!mangled_stack)
225     return;
226
227   for (i=0;i<mangled_stack_len;i++)
228     if (strcmp(s,mangled_stack[i]) == 0)
229       {
230         array_promote(mangled_stack[0],sizeof(fstring),i);      
231         return;
232       }
233
234   memmove(mangled_stack[1],mangled_stack[0],
235           sizeof(fstring)*MIN(mangled_stack_len,mangled_stack_size-1));
236   strcpy(mangled_stack[0],s);
237   p = strrchr(mangled_stack[0],'.');
238   if (p && (!strhasupper(p+1)) && (strlen(p+1) < 4))
239     *p = 0;
240   mangled_stack_len = MIN(mangled_stack_size,mangled_stack_len+1);
241 }
242
243 /****************************************************************************
244 check for a name on the mangled name stack
245 ****************************************************************************/
246 BOOL check_mangled_stack(char *s)
247 {
248   int i;
249   pstring tmpname;
250   char extension[5];
251   char *p = strrchr(s,'.');
252   BOOL check_extension = False;
253
254   extension[0] = 0;
255
256   if (!mangled_stack) return(False);
257
258   if (p)
259     {
260       check_extension = True;
261       StrnCpy(extension,p,4);
262       strlower(extension); /* XXXXXXX */
263     }
264
265   for (i=0;i<mangled_stack_len;i++)
266     {
267       strcpy(tmpname,mangled_stack[i]);
268       mangle_name_83(tmpname);
269       if (strequal(tmpname,s))
270         {
271           strcpy(s,mangled_stack[i]);
272           break;
273         }
274       if (check_extension && !strchr(mangled_stack[i],'.'))
275         {
276           strcpy(tmpname,mangled_stack[i]);
277           strcat(tmpname,extension);
278           mangle_name_83(tmpname);
279           if (strequal(tmpname,s))
280             {
281               strcpy(s,mangled_stack[i]);
282               strcat(s,extension);
283               break;
284             }     
285         }
286     }
287
288   if (i < mangled_stack_len)
289     {
290       DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i]));
291       array_promote(mangled_stack[0],sizeof(fstring),i);
292       return(True);      
293     }
294
295   return(False);
296 }       
297
298 static char *map_filename(char *s, /* This is null terminated */
299                           char *pattern, /* This isn't. */
300                           int len) /* This is the length of pattern. */
301 {
302   static pstring matching_bit;  /* The bit of the string which matches */
303                                 /* a * in pattern if indeed there is a * */
304   char *sp;                     /* Pointer into s. */
305   char *pp;                     /* Pointer into p. */
306   char *match_start;            /* Where the matching bit starts. */
307   pstring pat;
308
309   StrnCpy(pat, pattern, len);   /* Get pattern into a proper string! */
310   strcpy(matching_bit,"");      /* Match but no star gets this. */
311   pp = pat;                     /* Initialise the pointers. */
312   sp = s;
313   if ((len == 1) && (*pattern == '*')) {
314     return NULL;                /* Impossible, too ambiguous for */
315                                 /* words! */
316   }
317
318   while ((*sp)                  /* Not the end of the string. */
319          && (*pp)               /* Not the end of the pattern. */
320          && (*sp == *pp)        /* The two match. */
321          && (*pp != '*')) {     /* No wildcard. */
322     sp++;                       /* Keep looking. */
323     pp++;
324   }
325   if (!*sp && !*pp)             /* End of pattern. */
326     return matching_bit;        /* Simple match.  Return empty string. */
327   if (*pp == '*') {
328     pp++;                       /* Always interrested in the chacter */
329                                 /* after the '*' */
330     if (!*pp) {                 /* It is at the end of the pattern. */
331       StrnCpy(matching_bit, s, sp-s);
332       return matching_bit;
333     } else {
334       /* The next character in pattern must match a character further */
335       /* along s than sp so look for that character. */
336       match_start = sp;
337       while ((*sp)              /* Not the end of s. */
338              && (*sp != *pp))   /* Not the same  */
339         sp++;                   /* Keep looking. */
340       if (!*sp) {               /* Got to the end without a match. */
341         return NULL;
342       } else {                  /* Still hope for a match. */
343         /* Now sp should point to a matching character. */
344         StrnCpy(matching_bit, match_start, sp-match_start);
345         /* Back to needing a stright match again. */
346         while ((*sp)            /* Not the end of the string. */
347                && (*pp)         /* Not the end of the pattern. */
348                && (*sp == *pp)) { /* The two match. */
349           sp++;                 /* Keep looking. */
350           pp++;
351         }
352         if (!*sp && !*pp)       /* Both at end so it matched */
353           return matching_bit;
354         else
355           return NULL;
356       }
357     }
358   }
359   return NULL;                  /* No match. */
360 }
361
362
363 /* this is the magic char used for mangling */
364 char magic_char = '~';
365
366
367 /****************************************************************************
368 determine whther is name could be a mangled name
369 ****************************************************************************/
370 BOOL is_mangled(char *s)
371 {
372   char *m = strchr(s,magic_char);
373   if (!m) return(False);
374
375   /* we use two base 36 chars efore the extension */
376   if (m[1] == '.' || m[1] == 0 ||
377       m[2] == '.' || m[2] == 0 ||
378       (m[3] != '.' && m[3] != 0))
379     return(is_mangled(m+1));
380
381   /* it could be */
382   return(True);
383 }
384
385
386
387 /****************************************************************************
388 return a base 36 character. v must be from 0 to 35.
389 ****************************************************************************/
390 static char base36(unsigned int v)
391 {
392   static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
393   return basechars[v % 36];
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     if (!*start)
422       continue;                 /* Always check for the end. */
423     start++;                    /* Skip the ( */
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(lp_client_code_page() == KANJI_CODEPAGE)
514         {
515           if (is_shift_jis (*p))
516           {
517             if (extlen < 2)
518             {
519               extension[extlen++] = p[0];
520               extension[extlen++] = p[1];
521             }
522             else 
523             {
524               extension[extlen++] = base36 (((unsigned char) *p) % 36);
525             }
526             p += 2;
527           }
528           else if (is_kana (*p))
529           {
530             extension[extlen++] = p[0];
531             p++;
532           }
533           else 
534           {
535             if (isdoschar (*p) && *p != '.')
536               extension[extlen++] = p[0];
537             p++;
538           }
539         }
540         else
541         {
542           if (isdoschar(*p) && *p != '.')
543             extension[extlen++] = *p;
544           p++;
545         }
546       }
547           extension[extlen] = 0;
548     }
549   }
550
551   p = s;
552
553   while (*p && baselen < 5)
554   {
555     if(lp_client_code_page() == KANJI_CODEPAGE)
556     {
557       if (is_shift_jis (*p))
558       {
559         if (baselen < 4)
560         {
561           base[baselen++] = p[0];
562           base[baselen++] = p[1];
563         }
564         else 
565         {
566               base[baselen++] = base36 (((unsigned char) *p) % 36);
567         }
568         p += 2;
569       }
570       else if (is_kana (*p))
571       {
572         base[baselen++] = p[0];
573         p++;
574       }
575       else 
576       {
577         if (isdoschar (*p) && *p != '.')
578           base[baselen++] = p[0];
579         p++;
580       }
581     }
582     else
583     {
584       if (isdoschar(*p) && *p != '.')
585         base[baselen++] = *p;
586       p++;
587     }
588   }
589   base[baselen] = 0;
590
591   csum = csum % (36*36);
592
593     sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36));
594
595   if (*extension)
596     {
597       strcat(s,".");
598       strcat(s,extension);
599     }
600   DEBUG(5,("%s\n",s));
601 }
602
603
604
605 /*******************************************************************
606   work out if a name is illegal, even for long names
607   ******************************************************************/
608 static BOOL illegal_name(char *name)
609 {
610   static unsigned char illegal[256];
611   static BOOL initialised=False;
612   unsigned char *s;
613
614   if (!initialised) 
615   {
616     char *ill = "*\\/?<>|\":{}";
617     initialised = True;
618   
619     bzero((char *)illegal,256);
620     for (s = (unsigned char *)ill; *s; s++)
621       illegal[*s] = True;
622   }
623
624   if(lp_client_code_page() == KANJI_CODEPAGE)
625   {
626     for (s = (unsigned char *)name; *s;) {
627       if (is_shift_jis (*s)) {
628         s += 2;
629       } else if (illegal[*s]) {
630         return(True);
631       } else {
632         s++;
633       }
634     }
635   }
636   else
637   {
638     for (s = (unsigned char *)name;*s;s++)
639       if (illegal[*s]) return(True);
640   }
641
642   return(False);
643 }
644
645
646 /****************************************************************************
647 convert a filename to DOS format. return True if successful.
648 ****************************************************************************/
649 BOOL name_map_mangle(char *OutName,BOOL need83,int snum)
650 {
651 #ifdef MANGLE_LONG_FILENAMES
652   if (!need83 && illegal_name(OutName)) need83 = True;
653 #endif  
654
655   /* apply any name mappings */
656   {
657     char *map = lp_mangled_map(snum);
658     if (map && *map)
659       do_fwd_mangled_map(OutName,map);
660   }
661
662   /* check if it's already in 8.3 format */
663   if (need83 && !is_8_3(OutName, True)) {
664     if (!lp_manglednames(snum)) return(False);
665
666     /* mangle it into 8.3 */
667     push_mangled_name(OutName);  
668     mangle_name_83(OutName);
669   }
670   
671   return(True);
672 }
673