]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/mangle.c
fix demangling for pre OSX clients, after cnid_resolve id contains the DID
[netatalk.git] / etc / afpd / mangle.c
1 /* 
2  * $Id: mangle.c,v 1.16.2.1.2.7 2003-11-03 03:10:01 bfernhomberg 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
21 #define hextoint( c )   ( isdigit( c ) ? c - '0' : c + 10 - 'A' )
22 #define isuxdigit(x)    (isdigit(x) || (isupper(x) && isxdigit(x)))
23
24
25
26 static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
27 {
28     u_int16_t flags;
29     static char buffer[MAXPATHLEN +1];
30     size_t len;
31     char *u;
32
33     /* We need to check, whether we really need to demangle the filename        */
34     /* i.e. it's not just a file with a valid #HEX in the name ...              */
35     /* but we don't want to miss valid demangle as well.                        */
36
37     /* check whether file extensions match */
38     if ( NULL != (u = strchr(uname, '.')) ) {
39         if (strcmp(ext,u))
40                 return mfilename;
41     }
42     else if ( *ext == '.' )
43         return mfilename;
44
45     /* First we convert the unix name to our volume maccharset          */
46     /* This assumes, OSX will not send us a mangled name for *any*      */
47     /* other reason than a hint/filename mismatch on the OSX side!! */
48     /* If the filename needed mangling, we'll get the mac filename      */
49     /* till the first unconvertable char, so these have to      match   */
50     /* the mangled name we got ..                                       */
51
52     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
53     if ( (size_t) -1 == (len = convert_charset(vol->v_volcharset, vol->v_maccharset, 0, 
54                                       uname, strlen(uname), buffer, MAXPATHLEN, &flags)) ) {
55         return mfilename;
56     }
57     /* If the filename is too long we also needed to mangle isn't this stuff always false */
58     if ( len >= vol->max_filename ) {
59         flags |= CONV_REQMANGLE;
60         len = prefix;
61     }
62     
63     /* Ok, mangling was needed, now we do some further checks    */
64     /* this is still necessary, as we might have a file abcde:xx */
65     /* with id 12, mangled to abcde#12, and a passed filename    */
66     /* abcd#12                                               */ 
67     /* if we only checked if "prefix" number of characters match */
68     /* we get a false postive in above case                          */
69
70     if ( flags & CONV_REQMANGLE ) {
71         if (len) { 
72             /* convert the buffer to UTF8_MAC ... */
73             if ((size_t) -1 == (len = convert_charset(vol->v_maccharset, CH_UTF8_MAC, 0, 
74                                 buffer, len, buffer, MAXPATHLEN, &flags)) ) {
75                 return mfilename;
76             }
77             /* Now compare the two names, they have to match the number of characters in buffer */
78             /* prefix can be longer than len, OSX might send us the first character(s) of a     */
79             /* decomposed char as the *last* character(s) before the #, so our match below will */
80             /* still work, but leaves room for a race ... FIXME                             */
81             if ( prefix >= len && !strncmp (mfilename, buffer, len)) {
82                  return uname;
83             }
84         }
85         else {
86             /* We couldn't convert the name to maccharset at all, so we'd expect a name */
87             /* in the "???#ID" form ... */
88             if ( !strncmp("???", mfilename, prefix)) {
89                 return uname;
90             }
91             /* ..but OSX might send us only the first characters of a decomposed character. */
92             /*  So convert to UTF8_MAC again, now at least the prefix number of           */
93             /* characters have to match ... again a possible race FIXME                   */
94             
95             if ( (size_t) -1 == (len = convert_charset(vol->v_volcharset, CH_UTF8_MAC, 0, 
96                                   uname, strlen(uname), buffer, MAXPATHLEN, &flags)) ) {
97                 return mfilename;
98             }
99
100             if ( !strncmp (mfilename, buffer, prefix) ) {
101                 return uname;
102             }
103         }
104     }
105     return mfilename;
106 }
107
108 /* -------------------------------------------------------
109 */
110 static char *
111 private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx) 
112 {
113     char *t;
114     char *u_name;
115     u_int32_t id, file_id;
116     static char buffer[12 + MAXPATHLEN + 1];
117     int len = 12 + MAXPATHLEN + 1;
118     struct dir  *dir;
119     size_t prefix;
120
121     id = file_id = 0;
122
123     t = strchr(mfilename, MANGLE_CHAR);
124     if (t == NULL) {
125         return mfilename;
126     }
127     prefix = t - mfilename;
128     /* FIXME 
129      * is prefix == 0 a valid mangled filename ?
130     */
131     /* may be a mangled filename */
132     t++;
133     if (*t == '0') { /* can't start with a 0 */
134         return mfilename;
135     }
136     while(isuxdigit(*t)) {
137         id = (id *16) + hextoint(*t);
138         t++;
139     }
140     if ((*t != 0 && *t != '.') || strlen(t) > MAX_EXT_LENGTH || id < 17) {
141         return mfilename;
142     }
143
144     file_id = id = htonl(id);
145     if (osx) {
146         *osx = id;
147     }
148
149     /* is it a dir?, there's a conflict with pre OSX 'trash #2'  */
150     if ((dir = dirsearch(vol, id))) {
151         if (dir->d_did != id) {
152             /* not in the same folder, there's a race with outdate cache
153              * but we have to live with it, hopefully client will recover
154             */
155             return mfilename;
156         }
157         if (!osx) {
158             /* it's not from cname so mfilename and dir must be the same */
159             if (!strcmp(dir->d_m_name, mfilename)) {
160                 return dir->d_u_name;
161             }
162         } 
163         else {
164             return demangle_checks (vol, dir->d_u_name, mfilename, prefix, t);
165         }
166     }
167     else if (NULL != (u_name = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
168         if (id != did) {
169             return mfilename;
170         }
171         if (!osx) {
172             /* convert back to mac name and check it's the same */
173             t = utompath(vol, u_name, file_id, utf8_encoding());
174             if (!strcmp(t, mfilename)) {
175                 return u_name;
176             }
177         }
178         else {
179             return demangle_checks (vol, u_name, mfilename, prefix, t);
180         }
181     }
182
183     return mfilename;
184 }
185
186 /* -------------------------------------------------------
187 */
188 char *
189 demangle(const struct vol *vol, char *mfilename, cnid_t did)
190 {
191     return private_demangle(vol, mfilename, did, NULL);
192 }
193
194 /* -------------------------------------------------------
195  * OS X  
196 */
197 char *
198 demangle_osx(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *fileid) 
199 {
200     return private_demangle(vol, mfilename, did, fileid);
201 }
202
203 /* -----------------------
204    with utf8 filename not always round trip
205    filename   mac filename too long or first chars if unmatchable chars.
206    uname      unix filename 
207    id         file/folder ID or 0
208    
209 */
210 char *
211 mangle(const struct vol *vol, char *filename, char *uname, cnid_t id, int flags) {
212     char *ext = NULL;
213     char *m = NULL;
214     static char mfilename[MAX_LENGTH + 1];
215     char mangle_suffix[MANGLE_LENGTH + 1];
216     size_t ext_len = 0;
217     int k;
218
219     /* Do we really need to mangle this filename? */
220     if (!flags && strlen(filename) <= vol->max_filename) {
221         return filename;
222     }
223     /* First, attempt to locate a file extension. */
224     if (NULL != (ext = strrchr(uname, '.')) ) {
225         ext_len = strlen(ext);
226         if (ext_len > MAX_EXT_LENGTH) {
227             /* Do some bounds checking to prevent an extension overflow. */
228             ext_len = MAX_EXT_LENGTH;
229         }
230     }
231     m = mfilename;
232     memset(m, 0, MAX_LENGTH + 1);
233     k = sprintf(mangle_suffix, "%c%X", MANGLE_CHAR, ntohl(id));
234
235     strncpy(m, filename, MAX_LENGTH - k - ext_len);
236     if (*m == 0) {
237         strcat(m, "???");
238     }
239     strcat(m, mangle_suffix);
240     if (ext) {
241         strncat(m, ext, ext_len);
242     }
243
244     return m;
245 }