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