]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/mangle.c
Import FCE
[netatalk.git] / etc / afpd / mangle.c
1 /* 
2  * $Id: mangle.c,v 1.19 2006-09-19 01:35:45 didg Exp $ 
3  *
4  * Copyright (c) 2002. Joe Marcus Clarke (marcus@marcuscom.com)
5  * All Rights Reserved.  See COPYRIGHT.
6  *
7  * mangle, demangle (filename):
8  * mangle or demangle filenames if they are greater than the max allowed
9  * characters for a given version of AFP.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif /* HAVE_CONFIG_H */
15
16 #include <stdio.h>
17 #include <ctype.h>
18 #include "mangle.h"
19 #include "desktop.h"
20 #include <atalk/util.h>  
21
22 #define hextoint( c )   ( isdigit( c ) ? c - '0' : c + 10 - 'A' )
23 #define isuxdigit(x)    (isdigit(x) || (isupper(x) && isxdigit(x)))
24
25 static size_t mangle_extension(const struct vol *vol, const char* uname,
26                                char* extension, charset_t charset)
27 {
28   char *p = strrchr(uname, '.');
29
30   if (p && p != uname) {
31     u_int16_t flags = CONV_FORCE | CONV_UNESCAPEHEX;
32     size_t len = convert_charset(vol->v_volcharset, charset,
33                                  vol->v_maccharset, p, strlen(p),
34                                  extension, MAX_EXT_LENGTH, &flags);
35
36     if (len != (size_t)-1) return len;
37   }
38   return 0;
39 }
40
41 static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
42 {
43     u_int16_t flags;
44     static char buffer[MAXPATHLEN +2];  /* for convert_charset dest_len parameter +2 */
45     size_t len;
46     size_t mfilenamelen;
47
48     /* We need to check, whether we really need to demangle the filename        */
49     /* i.e. it's not just a file with a valid #HEX in the name ...              */
50     /* but we don't want to miss valid demangle as well.                        */
51
52     /* check whether file extensions match */
53     {
54       char buf[MAX_EXT_LENGTH + 2];  /* for convert_charset dest_len parameter +2 */
55       size_t ext_len = mangle_extension(vol, uname, buf, CH_UTF8_MAC);
56
57       if (ext_len) {
58         buf[ext_len] = '\0';
59         if (strcmp(ext, buf)) return mfilename;
60       } else {
61         if (*ext) return mfilename;
62       }
63     }
64
65     /* First we convert the unix name to our volume maccharset          */
66     /* This assumes, OSX will not send us a mangled name for *any*      */
67     /* other reason than a hint/filename mismatch on the OSX side!! */
68     /* If the filename needed mangling, we'll get the mac filename      */
69     /* till the first unconvertable char, so these have to      match   */
70     /* the mangled name we got ..                                       */
71
72     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
73     if ( (size_t) -1 == (len = convert_charset(vol->v_volcharset, vol->v_maccharset, 0, 
74                                       uname, strlen(uname), buffer, MAXPATHLEN, &flags)) ) {
75         return mfilename;
76     }
77     /* If the filename is too long we also needed to mangle */
78     mfilenamelen = strlen(mfilename);
79     if ( len >= vol->max_filename || mfilenamelen == MACFILELEN ) {
80         flags |= CONV_REQMANGLE;
81         len = prefix;
82     }
83     
84     /* Ok, mangling was needed, now we do some further checks    */
85     /* this is still necessary, as we might have a file abcde:xx */
86     /* with id 12, mangled to abcde#12, and a passed filename    */
87     /* abcd#12                                               */ 
88     /* if we only checked if "prefix" number of characters match */
89     /* we get a false postive in above case                          */
90
91     if ( (flags & CONV_REQMANGLE) ) {
92         if (len) { 
93             /* convert the buffer to UTF8_MAC ... */
94             if ((size_t) -1 == (len = convert_charset(vol->v_maccharset, CH_UTF8_MAC, 0, 
95                                 buffer, len, buffer, MAXPATHLEN, &flags)) ) {
96                 return mfilename;
97             }
98             /* Now compare the two names, they have to match the number of characters in buffer */
99             /* prefix can be longer than len, OSX might send us the first character(s) of a     */
100             /* decomposed char as the *last* character(s) before the #, so our match below will */
101             /* still work, but leaves room for a race ... FIXME                             */
102             if ( (prefix >= len || mfilenamelen == MACFILELEN) 
103                  && !strncmp (mfilename, buffer, len)) {
104                  return uname;
105             }
106         }
107         else {
108             /* We couldn't convert the name to maccharset at all, so we'd expect a name */
109             /* in the "???#ID" form ... */
110             if ( !strncmp("???", mfilename, prefix)) {
111                 return uname;
112             }
113             /* ..but OSX might send us only the first characters of a decomposed character. */
114             /*  So convert to UTF8_MAC again, now at least the prefix number of           */
115             /* characters have to match ... again a possible race FIXME                   */
116             
117             if ( (size_t) -1 == (len = convert_charset(vol->v_volcharset, CH_UTF8_MAC, 0, 
118                                   uname, strlen(uname), buffer, MAXPATHLEN, &flags)) ) {
119                 return mfilename;
120             }
121
122             if ( !strncmp (mfilename, buffer, prefix) ) {
123                 return uname;
124             }
125         }
126     }
127     return mfilename;
128 }
129
130 /* -------------------------------------------------------
131 */
132 static char *
133 private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx) 
134 {
135     char *t;
136     char *u_name;
137     u_int32_t id, file_id;
138     static char buffer[12 + MAXPATHLEN + 1];
139     int len = 12 + MAXPATHLEN + 1;
140     struct dir  *dir;
141     size_t prefix;
142
143     id = file_id = 0;
144
145     t = strchr(mfilename, MANGLE_CHAR);
146     if (t == NULL) {
147         return mfilename;
148     }
149     prefix = t - mfilename;
150     /* FIXME 
151      * is prefix == 0 a valid mangled filename ?
152     */
153     /* may be a mangled filename */
154     t++;
155     if (*t == '0') { /* can't start with a 0 */
156         return mfilename;
157     }
158     while(isuxdigit(*t)) {
159         id = (id *16) + hextoint(*t);
160         t++;
161     }
162     if ((*t != 0 && *t != '.') || strlen(t) > MAX_EXT_LENGTH || id < 17) {
163         return mfilename;
164     }
165
166     file_id = id = htonl(id);
167     if (osx) {
168         *osx = id;
169     }
170
171     /* is it a dir?, there's a conflict with pre OSX 'trash #2'  */
172     if ((dir = dirsearch(vol, id))) {
173         if (dir->d_parent && dir->d_parent->d_did != did) {
174             /* not in the same folder, there's a race with outdate cache
175              * but we have to live with it, hopefully client will recover
176             */
177             return mfilename;
178         }
179         if (!osx) {
180             /* it's not from cname so mfilename and dir must be the same */
181             if (!strcmp(dir->d_m_name, mfilename)) {
182                 return dir->d_u_name;
183             }
184         } 
185         else {
186             return demangle_checks (vol, dir->d_u_name, mfilename, prefix, t);
187         }
188     }
189     else if (NULL != (u_name = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
190         if (id != did) {
191             return mfilename;
192         }
193         if (!osx) {
194             /* convert back to mac name and check it's the same */
195             t = utompath(vol, u_name, file_id, utf8_encoding());
196             if (!strcmp(t, mfilename)) {
197                 return u_name;
198             }
199         }
200         else {
201             return demangle_checks (vol, u_name, mfilename, prefix, t);
202         }
203     }
204
205     return mfilename;
206 }
207
208 /* -------------------------------------------------------
209 */
210 char *
211 demangle(const struct vol *vol, char *mfilename, cnid_t did)
212 {
213     return private_demangle(vol, mfilename, did, NULL);
214 }
215
216 /* -------------------------------------------------------
217  * OS X  
218 */
219 char *
220 demangle_osx(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *fileid) 
221 {
222     return private_demangle(vol, mfilename, did, fileid);
223 }
224
225
226 /* -----------------------
227    with utf8 filename not always round trip
228    filename   mac filename too long or first chars if unmatchable chars.
229    uname      unix filename 
230    id         file/folder ID or 0
231    
232 */
233 char *
234 mangle(const struct vol *vol, char *filename, size_t filenamelen, char *uname, cnid_t id, int flags) {
235     char *m = NULL;
236     static char mfilename[MAXPATHLEN]; /* way > maxlen */
237     char mangle_suffix[MANGLE_LENGTH + 1];
238     char ext[MAX_EXT_LENGTH +2];  /* for convert_charset dest_len parameter +2 */
239     size_t ext_len;
240     size_t maxlen;
241     int k;
242     
243     maxlen = (flags & 2)?255:MACFILELEN; /* was vol->max_filename */
244     /* Do we really need to mangle this filename? */
245     if (!(flags & 1) && filenamelen <= maxlen) {
246         return filename;
247     }
248
249     if (!id) {
250         /* we don't have the file id! only catsearch call mangle with id == 0 */
251         return NULL;
252     }
253     /* First, attempt to locate a file extension. */
254     ext_len = mangle_extension(vol, uname, ext, (flags & 2) ? CH_UTF8_MAC : vol->v_maccharset);
255     m = mfilename;
256     k = sprintf(mangle_suffix, "%c%X", MANGLE_CHAR, ntohl(id));
257
258     if (filenamelen + k + ext_len > maxlen) {
259       u_int16_t opt = CONV_FORCE | CONV_UNESCAPEHEX;
260       size_t n = convert_charset(vol->v_volcharset,
261                                  (flags & 2) ? CH_UTF8_MAC : vol->v_maccharset,
262                                  vol->v_maccharset, uname, strlen(uname),
263                                  m, maxlen - k - ext_len, &opt);
264       m[n != (size_t)-1 ? n : 0] = 0;
265     } else {
266       strlcpy(m, filename, filenamelen + 1);
267     }
268     if (*m == 0) {
269         strcat(m, "???");
270     }
271     strcat(m, mangle_suffix);
272     if (ext_len) {
273         strncat(m, ext, ext_len);
274     }
275
276     return m;
277 }