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