2 * $Id: mangle.c,v 1.16.2.1.2.7 2003-11-03 03:10:01 bfernhomberg Exp $
4 * Copyright (c) 2002. Joe Marcus Clarke (marcus@marcuscom.com)
5 * All Rights Reserved. See COPYRIGHT.
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.
14 #endif /* HAVE_CONFIG_H */
21 #define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'A' )
22 #define isuxdigit(x) (isdigit(x) || (isupper(x) && isxdigit(x)))
26 static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
29 static char buffer[MAXPATHLEN +1];
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. */
37 /* check whether file extensions match */
38 if ( NULL != (u = strchr(uname, '.')) ) {
42 else if ( *ext == '.' )
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 .. */
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)) ) {
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;
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 */
67 /* if we only checked if "prefix" number of characters match */
68 /* we get a false postive in above case */
70 if ( flags & CONV_REQMANGLE ) {
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)) ) {
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)) {
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)) {
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 */
95 if ( (size_t) -1 == (len = convert_charset(vol->v_volcharset, CH_UTF8_MAC, 0,
96 uname, strlen(uname), buffer, MAXPATHLEN, &flags)) ) {
100 if ( !strncmp (mfilename, buffer, prefix) ) {
108 /* -------------------------------------------------------
111 private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx)
115 u_int32_t id, file_id;
116 static char buffer[12 + MAXPATHLEN + 1];
117 int len = 12 + MAXPATHLEN + 1;
123 t = strchr(mfilename, MANGLE_CHAR);
127 prefix = t - mfilename;
129 * is prefix == 0 a valid mangled filename ?
131 /* may be a mangled filename */
133 if (*t == '0') { /* can't start with a 0 */
136 while(isuxdigit(*t)) {
137 id = (id *16) + hextoint(*t);
140 if ((*t != 0 && *t != '.') || strlen(t) > MAX_EXT_LENGTH || id < 17) {
144 file_id = id = htonl(id);
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
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;
164 return demangle_checks (vol, dir->d_u_name, mfilename, prefix, t);
167 else if (NULL != (u_name = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
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)) {
179 return demangle_checks (vol, u_name, mfilename, prefix, t);
186 /* -------------------------------------------------------
189 demangle(const struct vol *vol, char *mfilename, cnid_t did)
191 return private_demangle(vol, mfilename, did, NULL);
194 /* -------------------------------------------------------
198 demangle_osx(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *fileid)
200 return private_demangle(vol, mfilename, did, fileid);
203 /* -----------------------
204 with utf8 filename not always round trip
205 filename mac filename too long or first chars if unmatchable chars.
207 id file/folder ID or 0
211 mangle(const struct vol *vol, char *filename, char *uname, cnid_t id, int flags) {
214 static char mfilename[MAX_LENGTH + 1];
215 char mangle_suffix[MANGLE_LENGTH + 1];
219 /* Do we really need to mangle this filename? */
220 if (!flags && strlen(filename) <= vol->max_filename) {
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;
232 memset(m, 0, MAX_LENGTH + 1);
233 k = sprintf(mangle_suffix, "%c%X", MANGLE_CHAR, ntohl(id));
235 strncpy(m, filename, MAX_LENGTH - k - ext_len);
239 strcat(m, mangle_suffix);
241 strncat(m, ext, ext_len);