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