]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/desktop.c
c594480a9819768ee712a900c0bd1f6877c4279d
[netatalk.git] / etc / afpd / desktop.c
1 /*
2  * $Id: desktop.c,v 1.38 2009-10-13 22:55:36 didg Exp $
3  *
4  * See COPYRIGHT.
5  *
6  * bug:
7  * afp_XXXcomment are (the only) functions able to open
8  * a ressource fork when there's no data fork, eg after
9  * it was removed with samba.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif /* HAVE_CONFIG_H */
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <ctype.h>
19
20 #include <errno.h>
21 #include <dirent.h>
22
23 #include <atalk/adouble.h>
24 #include <sys/uio.h>
25 #include <sys/param.h>
26 #include <sys/socket.h>
27 #include <netatalk/at.h>
28 #include <netatalk/endian.h>
29 #include <atalk/dsi.h>
30 #include <atalk/atp.h>
31 #include <atalk/asp.h>
32 #include <atalk/afp.h>
33 #include <atalk/util.h>
34 #include <atalk/logger.h>
35 #include "volume.h"
36 #include "directory.h"
37 #include "fork.h"
38 #include "globals.h"
39 #include "desktop.h"
40 #include "mangle.h"
41
42
43 int afp_opendt(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
44 {
45     struct vol  *vol;
46     u_int16_t   vid;
47
48     ibuf += 2;
49
50     memcpy( &vid, ibuf, sizeof(vid));
51     if (NULL == ( vol = getvolbyvid( vid )) ) {
52         *rbuflen = 0;
53         return( AFPERR_PARAM );
54     }
55
56     memcpy( rbuf, &vid, sizeof(vid));
57     *rbuflen = sizeof(vid);
58     return( AFP_OK );
59 }
60
61 int afp_closedt(AFPObj *obj _U_, char *ibuf _U_, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
62 {
63     *rbuflen = 0;
64     return( AFP_OK );
65 }
66
67 struct savedt   si = { { 0, 0, 0, 0 }, -1, 0, 0 };
68
69 static char *icon_dtfile(struct vol *vol, u_char creator[ 4 ])
70 {
71     return dtfile( vol, creator, ".icon" );
72 }
73
74 static int iconopen(struct vol *vol, u_char creator[ 4 ], int flags, int mode)
75 {
76     char        *dtf, *adt, *adts;
77
78     if ( si.sdt_fd != -1 ) {
79         if ( memcmp( si.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
80                 si.sdt_vid == vol->v_vid ) {
81             return 0;
82         }
83         close( si.sdt_fd );
84         si.sdt_fd = -1;
85     }
86
87     dtf = icon_dtfile( vol, creator);
88
89     if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
90         if ( errno == ENOENT && ( flags & O_CREAT )) {
91             if (( adts = strrchr( dtf, '/' )) == NULL ) {
92                 return -1;
93             }
94             *adts = '\0';
95             if (( adt = strrchr( dtf, '/' )) == NULL ) {
96                 return -1;
97             }
98             *adt = '\0';
99             (void) ad_mkdir( dtf, DIRBITS | 0777 );
100             *adt = '/';
101             (void) ad_mkdir( dtf, DIRBITS | 0777 );
102             *adts = '/';
103
104             if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
105                 LOG(log_error, logtype_afpd, "iconopen(%s): open: %s", dtf, strerror(errno) );
106                 return -1;
107             }
108         } else {
109             return -1;
110         }
111     }
112
113     memcpy( si.sdt_creator, creator, sizeof( CreatorType ));
114     si.sdt_vid = vol->v_vid;
115     si.sdt_index = 1;
116     return 0;
117 }
118
119 int afp_addicon(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
120 {
121     struct vol          *vol;
122 #ifndef NO_DDP
123     struct iovec        iov[ 2 ];
124 #endif
125     u_char              fcreator[ 4 ], imh[ 12 ], irh[ 12 ], *p;
126     int                 itype, cc = AFP_OK, iovcnt = 0, buflen;
127     u_int32_t           ftype, itag;
128     u_int16_t           bsize, rsize, vid;
129
130     buflen = *rbuflen;
131     *rbuflen = 0;
132     ibuf += 2;
133
134     memcpy( &vid, ibuf, sizeof( vid ));
135     ibuf += sizeof( vid );
136     if (NULL == ( vol = getvolbyvid( vid )) ) {
137         cc = AFPERR_PARAM;
138         goto addicon_err;
139     }
140
141     memcpy( fcreator, ibuf, sizeof( fcreator ));
142     ibuf += sizeof( fcreator );
143
144     memcpy( &ftype, ibuf, sizeof( ftype ));
145     ibuf += sizeof( ftype );
146
147     itype = (unsigned char) *ibuf;
148     ibuf += 2;
149
150     memcpy( &itag, ibuf, sizeof( itag ));
151     ibuf += sizeof( itag );
152
153     memcpy( &bsize, ibuf, sizeof( bsize ));
154     bsize = ntohs( bsize );
155
156     if ( si.sdt_fd != -1 ) {
157         (void)close( si.sdt_fd );
158         si.sdt_fd = -1;
159     }
160
161     if (iconopen( vol, fcreator, O_RDWR|O_CREAT, 0666 ) < 0) {
162         cc = AFPERR_NOITEM;
163         goto addicon_err;
164     }
165
166     if (lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0) {
167         close(si.sdt_fd);
168         si.sdt_fd = -1;
169         LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
170         cc = AFPERR_PARAM;
171         goto addicon_err;
172     }
173
174     /*
175      * Read icon elements until we find a match to replace, or
176      * we get to the end to insert.
177      */
178     p = imh;
179     memcpy( p, &itag, sizeof( itag ));
180     p += sizeof( itag );
181     memcpy( p, &ftype, sizeof( ftype ));
182     p += sizeof( ftype );
183     *p++ = itype;
184     *p++ = 0;
185     bsize = htons( bsize );
186     memcpy( p, &bsize, sizeof( bsize ));
187     bsize = ntohs( bsize );
188     while (( cc = read( si.sdt_fd, irh, sizeof( irh ))) > 0 ) {
189         memcpy( &rsize, irh + 10, sizeof( rsize ));
190         rsize = ntohs( rsize );
191         /*
192          * Is this our set of headers?
193          */
194         if ( memcmp( irh, imh, sizeof( irh ) - sizeof( u_short )) == 0 ) {
195             /*
196              * Is the size correct?
197              */
198             if ( bsize != rsize )
199                 cc = AFPERR_ITYPE;
200             break;
201         }
202
203         if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
204             LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator),strerror(errno) );
205             cc = AFPERR_PARAM;
206         }
207     }
208
209     /*
210      * Some error occurred, return.
211      */
212 addicon_err:
213     if ( cc < 0 ) {
214         if (obj->proto == AFPPROTO_DSI) {
215             dsi_writeinit(obj->handle, rbuf, buflen);
216             dsi_writeflush(obj->handle);
217         }
218         return cc;
219     }
220
221     switch (obj->proto) {
222 #ifndef NO_DDP
223     case AFPPROTO_ASP:
224         buflen = bsize;
225         if ((asp_wrtcont(obj->handle, rbuf, &buflen) < 0) || buflen != bsize)
226             return( AFPERR_PARAM );
227
228 #ifdef DEBUG1
229         if (obj->options.flags & OPTION_DEBUG) {
230             printf("(write) len: %d\n", buflen);
231             bprint(rbuf, buflen);
232         }
233 #endif
234
235         /*
236          * We're at the end of the file, add the headers, etc.  */
237         if ( cc == 0 ) {
238             iov[ 0 ].iov_base = (caddr_t)imh;
239             iov[ 0 ].iov_len = sizeof( imh );
240             iov[ 1 ].iov_base = rbuf;
241             iov[ 1 ].iov_len = bsize;
242             iovcnt = 2;
243         }
244
245         /*
246          * We found an icon to replace.
247          */
248         if ( cc > 0 ) {
249             iov[ 0 ].iov_base = rbuf;
250             iov[ 0 ].iov_len = bsize;
251             iovcnt = 1;
252         }
253
254         if ( writev( si.sdt_fd, iov, iovcnt ) < 0 ) {
255             LOG(log_error, logtype_afpd, "afp_addicon(%s): writev: %s", icon_dtfile(vol, fcreator), strerror(errno) );
256             return( AFPERR_PARAM );
257         }
258         break;
259 #endif /* no afp/asp */      
260     case AFPPROTO_DSI:
261         {
262             DSI *dsi = obj->handle;
263
264             iovcnt = dsi_writeinit(dsi, rbuf, buflen);
265
266             /* add headers at end of file */
267             if ((cc == 0) && (write(si.sdt_fd, imh, sizeof(imh)) < 0)) {
268                 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
269                 dsi_writeflush(dsi);
270                 return AFPERR_PARAM;
271             }
272
273             if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
274                 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
275                 dsi_writeflush(dsi);
276                 return AFPERR_PARAM;
277             }
278
279             while ((iovcnt = dsi_write(dsi, rbuf, buflen))) {
280 #ifdef DEBUG1
281                 if ( obj->options.flags & OPTION_DEBUG ) {
282                     printf("(write) command cont'd: %d\n", iovcnt);
283                     bprint(rbuf, iovcnt);
284                 }
285 #endif
286                 if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
287                     LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
288                     dsi_writeflush(dsi);
289                     return AFPERR_PARAM;
290                 }
291             }
292         }
293         break;
294     }
295
296     close( si.sdt_fd );
297     si.sdt_fd = -1;
298     return( AFP_OK );
299 }
300
301 static const u_char     utag[] = { 0, 0, 0, 0 };
302 static const u_char     ucreator[] = { 0, 0, 0, 0 };/* { 'U', 'N', 'I', 'X' };*/
303 static const u_char     utype[] = { 0, 0, 0, 0 };/* { 'T', 'E', 'X', 'T' };*/
304 static const short      usize = 256;
305
306 #if 0
307 static const u_char     uicon[] = {
308     0x1F, 0xFF, 0xFC, 0x00, 0x10, 0x00, 0x06, 0x00,
309     0x10, 0x00, 0x05, 0x00, 0x10, 0x00, 0x04, 0x80,
310     0x10, 0x00, 0x04, 0x40, 0x10, 0x00, 0x04, 0x20,
311     0x10, 0x00, 0x07, 0xF0, 0x10, 0x00, 0x00, 0x10,
312     0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,
313     0x10, 0x00, 0x00, 0x10, 0x10, 0x80, 0x02, 0x10,
314     0x11, 0x80, 0x03, 0x10, 0x12, 0x80, 0x02, 0x90,
315     0x12, 0x80, 0x02, 0x90, 0x14, 0x80, 0x02, 0x50,
316     0x14, 0x87, 0xC2, 0x50, 0x14, 0x58, 0x34, 0x50,
317     0x14, 0x20, 0x08, 0x50, 0x12, 0x16, 0xD0, 0x90,
318     0x11, 0x01, 0x01, 0x10, 0x12, 0x80, 0x02, 0x90,
319     0x12, 0x9C, 0x72, 0x90, 0x14, 0x22, 0x88, 0x50,
320     0x14, 0x41, 0x04, 0x50, 0x14, 0x49, 0x24, 0x50,
321     0x14, 0x55, 0x54, 0x50, 0x14, 0x5D, 0x74, 0x50,
322     0x14, 0x5D, 0x74, 0x50, 0x12, 0x49, 0x24, 0x90,
323     0x12, 0x22, 0x88, 0x90, 0x1F, 0xFF, 0xFF, 0xF0,
324     0x1F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFE, 0x00,
325     0x1F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80,
326     0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0,
327     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
328     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
329     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
330     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
331     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
332     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
333     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
334     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
335     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
336     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
337     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
338     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
339     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
340 };
341 #endif
342
343 int afp_geticoninfo(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
344 {
345     struct vol  *vol;
346     u_char      fcreator[ 4 ], ih[ 12 ];
347     u_int16_t   vid, iindex, bsize;
348
349     *rbuflen = 0;
350     ibuf += 2;
351
352     memcpy( &vid, ibuf, sizeof( vid ));
353     ibuf += sizeof( vid );
354     if (NULL == ( vol = getvolbyvid( vid )) ) {
355         return( AFPERR_PARAM );
356     }
357
358     memcpy( fcreator, ibuf, sizeof( fcreator ));
359     ibuf += sizeof( fcreator );
360     memcpy( &iindex, ibuf, sizeof( iindex ));
361     iindex = ntohs( iindex );
362
363     if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 ) {
364         if ( iindex > 1 ) {
365             return( AFPERR_NOITEM );
366         }
367         memcpy( ih, utag, sizeof( utag ));
368         memcpy( ih + sizeof( utag ), utype, sizeof( utype ));
369         *( ih + sizeof( utag ) + sizeof( utype )) = 1;
370         *( ih + sizeof( utag ) + sizeof( utype ) + 1 ) = 0;
371         memcpy( ih + sizeof( utag ) + sizeof( utype ) + 2, &usize,
372                 sizeof( usize ));
373         memcpy( rbuf, ih, sizeof( ih ));
374         *rbuflen = sizeof( ih );
375         return( AFP_OK );
376     }
377
378     if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
379         return( AFPERR_NOITEM );
380     }
381
382     if ( iindex < si.sdt_index ) {
383         if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
384             return( AFPERR_PARAM );
385         }
386         si.sdt_index = 1;
387     }
388
389     /*
390      * Position to the correct spot.
391      */
392     for (;;) {
393         if ( read( si.sdt_fd, ih, sizeof( ih )) != sizeof( ih )) {
394             close( si.sdt_fd );
395             si.sdt_fd = -1;
396             return( AFPERR_NOITEM );
397         }
398         memcpy( &bsize, ih + 10, sizeof( bsize ));
399         bsize = ntohs(bsize);
400         if ( lseek( si.sdt_fd, (off_t) bsize, SEEK_CUR ) < 0 ) {
401             LOG(log_error, logtype_afpd, "afp_iconinfo(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
402             return( AFPERR_PARAM );
403         }
404         if ( si.sdt_index == iindex ) {
405             memcpy( rbuf, ih, sizeof( ih ));
406             *rbuflen = sizeof( ih );
407             return( AFP_OK );
408         }
409         si.sdt_index++;
410     }
411 }
412
413
414 int afp_geticon(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
415 {
416     struct vol  *vol;
417     off_t       offset;
418     int         rc, buflen;
419     u_char      fcreator[ 4 ], ftype[ 4 ], itype, ih[ 12 ];
420     u_int16_t   vid, bsize, rsize;
421
422     buflen = *rbuflen;
423     *rbuflen = 0;
424     ibuf += 2;
425
426     memcpy( &vid, ibuf, sizeof( vid ));
427     ibuf += sizeof( vid );
428     if (NULL == ( vol = getvolbyvid( vid )) ) {
429         return( AFPERR_PARAM );
430     }
431
432     memcpy( fcreator, ibuf, sizeof( fcreator ));
433     ibuf += sizeof( fcreator );
434     memcpy( ftype, ibuf, sizeof( ftype ));
435     ibuf += sizeof( ftype );
436     itype = (unsigned char) *ibuf++;
437     ibuf++;
438     memcpy( &bsize, ibuf, sizeof( bsize ));
439     bsize = ntohs( bsize );
440
441 #if 0
442     if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 &&
443             memcmp( ftype, utype, sizeof( utype )) == 0 &&
444             itype == 1 &&
445             bsize <= usize ) {
446         memcpy( rbuf, uicon, bsize);
447         *rbuflen = bsize;
448         return( AFP_OK );
449     }
450 #endif
451
452     if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
453         return( AFPERR_NOITEM );
454     }
455
456     if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
457         close(si.sdt_fd);
458         si.sdt_fd = -1;
459         LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno));
460         return( AFPERR_PARAM );
461     }
462
463     si.sdt_index = 1;
464     offset = 0;
465     while (( rc = read( si.sdt_fd, ih, sizeof( ih ))) > 0 ) {
466         si.sdt_index++;
467         offset += sizeof(ih);
468         if ( memcmp( ih + sizeof( int ), ftype, sizeof( ftype )) == 0 &&
469                 *(ih + sizeof( int ) + sizeof( ftype )) == itype ) {
470             break;
471         }
472         memcpy( &rsize, ih + 10, sizeof( rsize ));
473         rsize = ntohs( rsize );
474         if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
475             LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
476             return( AFPERR_PARAM );
477         }
478         offset += rsize;
479     }
480
481     if ( rc < 0 ) {
482         LOG(log_error, logtype_afpd, "afp_geticon(%s): read: %s", icon_dtfile(vol, fcreator), strerror(errno));
483         return( AFPERR_PARAM );
484     }
485
486     if ( rc == 0 ) {
487         return( AFPERR_NOITEM );
488     }
489
490     memcpy( &rsize, ih + 10, sizeof( rsize ));
491     rsize = ntohs( rsize );
492 #define min(a,b)        ((a)<(b)?(a):(b))
493     rc = min( bsize, rsize );
494
495     if ((obj->proto == AFPPROTO_DSI) && (buflen < rc)) {
496         DSI *dsi = obj->handle;
497         struct stat st;
498         off_t size;
499
500         size = (fstat(si.sdt_fd, &st) < 0) ? 0 : st.st_size;
501         if (size < rc + offset) {
502             return AFPERR_PARAM;
503         }
504
505         if ((*rbuflen = dsi_readinit(dsi, rbuf, buflen, rc, AFP_OK)) < 0)
506             goto geticon_exit;
507
508         /* do to the streaming nature, we have to exit if we encounter
509          * a problem. much confusion results otherwise. */
510         while (*rbuflen > 0) {
511 #ifdef WITH_SENDFILE
512             if (!obj->options.flags & OPTION_DEBUG) {
513                 if (sys_sendfile(dsi->socket, si.sdt_fd, &offset, dsi->datasize) < 0) {
514                     switch (errno) {
515                     case ENOSYS:
516                     case EINVAL:  /* there's no guarantee that all fs support sendfile */
517                         break;
518                     default:
519                         goto geticon_exit;
520                     }
521                 }
522                 goto geticon_done;
523             }
524 #endif
525             buflen = read(si.sdt_fd, rbuf, *rbuflen);
526             if (buflen < 0)
527                 goto geticon_exit;
528
529 #ifdef DEBUG1
530             if (obj->options.flags & OPTION_DEBUG) {
531                 printf( "(read) reply: %d, %d\n", buflen, dsi->clientID);
532                 bprint(rbuf, buflen);
533             }
534 #endif
535             /* dsi_read() also returns buffer size of next allocation */
536             buflen = dsi_read(dsi, rbuf, buflen); /* send it off */
537             if (buflen < 0)
538                 goto geticon_exit;
539
540             *rbuflen = buflen;
541         }
542
543 geticon_done:
544         dsi_readdone(dsi);
545         return AFP_OK;
546
547 geticon_exit:
548         LOG(log_info, logtype_afpd, "afp_geticon(%s): %s", icon_dtfile(vol, fcreator), strerror(errno));
549         dsi_readdone(dsi);
550         obj->exit(EXITERR_SYS);
551         return AFP_OK;
552
553     } else {
554         if ( read( si.sdt_fd, rbuf, rc ) < rc ) {
555             return( AFPERR_PARAM );
556         }
557         *rbuflen = rc;
558     }
559     return AFP_OK;
560 }
561
562 /* ---------------------- */
563 static const char               hexdig[] = "0123456789abcdef";
564 char *dtfile(const struct vol *vol, u_char creator[], char *ext )
565 {
566     static char path[ MAXPATHLEN + 1];
567     char        *p;
568     unsigned int i;
569
570     strcpy( path, vol->v_path );
571     strcat( path, "/.AppleDesktop/" );
572     for ( p = path; *p != '\0'; p++ )
573         ;
574
575     if ( !isprint( creator[ 0 ] ) || creator[ 0 ] == '/' ) {
576         *p++ = hexdig[ ( creator[ 0 ] & 0xf0 ) >> 4 ];
577         *p++ = hexdig[ creator[ 0 ] & 0x0f ];
578     } else {
579         *p++ = creator[ 0 ];
580     }
581
582     *p++ = '/';
583
584     for ( i = 0; i < sizeof( CreatorType ); i++ ) {
585         if ( !isprint( creator[ i ] ) || creator[ i ] == '/' ) {
586             *p++ = hexdig[ ( creator[ i ] & 0xf0 ) >> 4 ];
587             *p++ = hexdig[ creator[ i ] & 0x0f ];
588         } else {
589             *p++ = creator[ i ];
590         }
591     }
592     *p = '\0';
593     strcat( path, ext );
594
595     return( path );
596 }
597
598 /* ---------------------------
599  * mpath is only a filename 
600  * did filename parent directory ID.
601 */
602
603 char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
604 {
605     static char  upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
606     char        *m, *u;
607     size_t       inplen;
608     size_t       outlen;
609     u_int16_t    flags = 0;
610         
611     if ( *mpath == '\0' ) {
612         return( "." );
613     }
614
615     /* set conversion flags */
616     if (!(vol->v_flags & AFPVOL_NOHEX))
617         flags |= CONV_ESCAPEHEX;
618     if (!(vol->v_flags & AFPVOL_USEDOTS))
619         flags |= CONV_ESCAPEDOTS;
620
621     if ((vol->v_casefold & AFPVOL_MTOUUPPER))
622         flags |= CONV_TOUPPER;
623     else if ((vol->v_casefold & AFPVOL_MTOULOWER))
624         flags |= CONV_TOLOWER;
625
626     if ((vol->v_flags & AFPVOL_EILSEQ)) {
627         flags |= CONV__EILSEQ;
628     }
629
630     m = demangle(vol, mpath, did);
631     if (m != mpath) {
632         return m;
633     }
634
635     m = mpath;
636     u = upath;
637
638     inplen = strlen(m);
639     outlen = MAXPATHLEN;
640
641     if ((size_t)-1 == (outlen = convert_charset ( (utf8)?CH_UTF8_MAC:vol->v_maccharset, vol->v_volcharset, vol->v_maccharset, m, inplen, u, outlen, &flags)) ) {
642         LOG(log_error, logtype_afpd, "conversion from %s to %s for %s failed.", (utf8)?"UTF8-MAC":vol->v_maccodepage, vol->v_volcodepage, mpath);
643             return NULL;
644     }
645
646 #ifdef DEBUG
647     LOG(log_debug, logtype_afpd, "mtoupath: '%s':'%s'", mpath, upath);
648 #endif /* DEBUG */
649     return( upath );
650 }
651
652 /* --------------- 
653  * id filename ID
654 */
655 char *utompath(const struct vol *vol, char *upath, cnid_t id, int utf8)
656 {
657     static char  mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
658     char        *m, *u;
659     u_int16_t    flags = CONV_IGNORE | CONV_UNESCAPEHEX;
660     size_t       outlen;
661
662     m = mpath;
663     outlen = strlen(upath);
664
665     if ((vol->v_casefold & AFPVOL_UTOMUPPER))
666         flags |= CONV_TOUPPER;
667     else if ((vol->v_casefold & AFPVOL_UTOMLOWER))
668         flags |= CONV_TOLOWER;
669
670     if ((vol->v_flags & AFPVOL_EILSEQ)) {
671         flags |= CONV__EILSEQ;
672     }
673
674     u = upath;
675
676     /* convert charsets */
677     if ((size_t)-1 == ( outlen = convert_charset ( vol->v_volcharset, (utf8)?CH_UTF8_MAC:vol->v_maccharset, vol->v_maccharset, u, outlen, mpath, MAXPATHLEN, &flags)) ) { 
678         LOG(log_error, logtype_afpd, "Conversion from %s to %s for %s (%u) failed.", vol->v_volcodepage, vol->v_maccodepage, u, ntohl(id));
679         goto utompath_error;
680     }
681
682     if (!(flags & CONV_REQMANGLE)) 
683         flags = 0;
684     else
685         flags = 1;
686
687     if (utf8)
688         flags |= 2;
689
690     m = mangle(vol, mpath, outlen, upath, id, flags);
691
692 #ifdef DEBUG
693     LOG(log_debug, logtype_afpd, "utompath: '%s':'%s':'%2.2X'", upath, m, ntohl(id));
694 #endif /* DEBUG */
695     return(m);
696
697 utompath_error:
698     u = "???";
699     m = mangle(vol, u, strlen(u), upath, id, (utf8)?3:1);
700     return(m);
701 }
702
703 /* ------------------------- */
704 static int ad_addcomment(struct vol *vol, struct path *path, char *ibuf)
705 {
706     struct ofork        *of;
707     char                *name, *upath;
708     int                 isadir;
709     int                 clen;
710     struct adouble      ad, *adp;
711
712     clen = (u_char)*ibuf++;
713     clen = min( clen, 199 );
714
715     upath = path->u_name;
716     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
717         return AFPERR_ACCESS;
718     }
719     
720     isadir = path_isadir(path);
721     if (isadir || !(of = of_findname(path))) {
722         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
723         adp = &ad;
724     } else
725         adp = of->of_ad;
726
727     if (ad_open_metadata( upath , vol_noadouble(vol) | ( (isadir) ? ADFLAGS_DIR :0),O_CREAT, adp) < 0 ) {
728         return( AFPERR_ACCESS );
729     }
730
731     if (ad_getentryoff(adp, ADEID_COMMENT)) {
732         if ( (ad_get_MD_flags( adp ) & O_CREAT) ) {
733             if ( *path->m_name == '\0' ) {
734                 name = curdir->d_m_name;
735             } else {
736                 name = path->m_name;
737             }
738             ad_setname(adp, name);
739         }
740         ad_setentrylen( adp, ADEID_COMMENT, clen );
741         memcpy( ad_entry( adp, ADEID_COMMENT ), ibuf, clen );
742         ad_flush( adp );
743     }
744     ad_close_metadata( adp);
745     return( AFP_OK );
746 }
747
748 /* ----------------------------- */
749 int afp_addcomment(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
750 {
751     struct vol          *vol;
752     struct dir          *dir;
753     struct path         *path;
754     u_int32_t           did;
755     u_int16_t           vid;
756
757     *rbuflen = 0;
758     ibuf += 2;
759
760     memcpy( &vid, ibuf, sizeof( vid ));
761     ibuf += sizeof( vid );
762     if (NULL == ( vol = getvolbyvid( vid )) ) {
763         return( AFPERR_PARAM );
764     }
765
766     memcpy( &did, ibuf, sizeof( did ));
767     ibuf += sizeof( did );
768     if (NULL == ( dir = dirlookup( vol, did )) ) {
769         return afp_errno;
770     }
771
772     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
773         return get_afp_errno(AFPERR_NOOBJ);
774     }
775
776     if ((u_long)ibuf & 1 ) {
777         ibuf++;
778     }
779
780     return ad_addcomment(vol, path, ibuf);
781 }
782
783 /* -------------------- */
784 static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, int *rbuflen)
785 {
786     struct adouble      ad, *adp;
787     struct ofork        *of;
788     char                *upath;
789     int                 isadir;
790     int                 clen;
791
792     upath = path->u_name;
793     isadir = path_isadir(path);
794     if (isadir || !(of = of_findname(path))) {
795         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
796         adp = &ad;
797     } else
798         adp = of->of_ad;
799         
800     if ( ad_metadata( upath,( isadir) ? ADFLAGS_DIR : 0, adp) < 0 ) {
801         return( AFPERR_NOITEM );
802     }
803
804     if (!ad_getentryoff(adp, ADEID_COMMENT)) {
805         ad_close_metadata( adp );
806         return AFPERR_NOITEM;
807     }
808     /*
809      * Make sure the AD file is not bogus.
810      */
811     if ( ad_getentrylen( adp, ADEID_COMMENT ) <= 0 ||
812             ad_getentrylen( adp, ADEID_COMMENT ) > 199 ) {
813         ad_close_metadata( adp );
814         return( AFPERR_NOITEM );
815     }
816
817     clen = min( ad_getentrylen( adp, ADEID_COMMENT ), 128 ); /* OSX only use 128, greater kill Adobe CS2 */
818     *rbuf++ = clen;
819     memcpy( rbuf, ad_entry( adp, ADEID_COMMENT ), clen);
820     *rbuflen = clen + 1;
821     ad_close_metadata( adp);
822
823     return( AFP_OK );
824 }
825
826 /* -------------------- */
827 int afp_getcomment(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
828 {
829     struct vol          *vol;
830     struct dir          *dir;
831     struct path         *s_path;
832     u_int32_t           did;
833     u_int16_t           vid;
834     
835     *rbuflen = 0;
836     ibuf += 2;
837
838     memcpy( &vid, ibuf, sizeof( vid ));
839     ibuf += sizeof( vid );
840     if (NULL == ( vol = getvolbyvid( vid )) ) {
841         return( AFPERR_PARAM );
842     }
843
844     memcpy( &did, ibuf, sizeof( did ));
845     ibuf += sizeof( did );
846     if (NULL == ( dir = dirlookup( vol, did )) ) {
847         return afp_errno;
848     }
849
850     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
851         return get_afp_errno(AFPERR_NOOBJ);
852     }
853
854     return ad_getcomment(vol, s_path, rbuf, rbuflen);
855 }
856
857 /* ----------------------- */
858 static int ad_rmvcomment(struct vol *vol, struct path *path)
859 {
860     struct adouble      ad, *adp;
861     struct ofork        *of;
862     int                 isadir;
863     char                *upath;
864
865     upath = path->u_name;
866     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
867         return AFPERR_ACCESS;
868     }
869
870     isadir = path_isadir(path);
871     if (isadir || !(of = of_findname(path))) {
872         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
873         adp = &ad;
874     } else
875         adp = of->of_ad;
876
877     if ( ad_open_metadata( upath, (isadir) ? ADFLAGS_DIR : 0, 0, adp) < 0 ) {
878         switch ( errno ) {
879         case ENOENT :
880             return( AFPERR_NOITEM );
881         case EACCES :
882             return( AFPERR_ACCESS );
883         default :
884             return( AFPERR_PARAM );
885         }
886     }
887
888     if (ad_getentryoff(adp, ADEID_COMMENT)) {
889         ad_setentrylen( adp, ADEID_COMMENT, 0 );
890         ad_flush( adp );
891     }
892     ad_close_metadata( adp);
893     return( AFP_OK );
894 }
895
896 /* ----------------------- */
897 int afp_rmvcomment(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
898 {
899     struct vol          *vol;
900     struct dir          *dir;
901     struct path         *s_path;
902     u_int32_t           did;
903     u_int16_t           vid;
904
905     *rbuflen = 0;
906     ibuf += 2;
907
908     memcpy( &vid, ibuf, sizeof( vid ));
909     ibuf += sizeof( vid );
910     if (NULL == ( vol = getvolbyvid( vid )) ) {
911         return( AFPERR_PARAM );
912     }
913
914     memcpy( &did, ibuf, sizeof( did ));
915     ibuf += sizeof( did );
916     if (NULL == ( dir = dirlookup( vol, did )) ) {
917         return afp_errno;
918     }
919
920     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
921         return get_afp_errno(AFPERR_NOOBJ);
922     }
923     
924     return ad_rmvcomment(vol, s_path);
925 }