]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/catsearch.c
Merge branch-2-1
[netatalk.git] / etc / afpd / catsearch.c
1 /* 
2  * Netatalk 2002 (c)
3  * Copyright (C) 1990, 1993 Regents of The University of Michigan
4  * All Rights Reserved. See COPYRIGHT
5  */
6
7
8 /*
9  * This file contains FPCatSearch implementation. FPCatSearch performs
10  * file/directory search based on specified criteria. It is used by client
11  * to perform fast searches on (propably) big volumes. So, it has to be
12  * pretty fast.
13  *
14  * This implementation bypasses most of adouble/libatalk stuff as long as
15  * possible and does a standard filesystem search. It calls higher-level
16  * libatalk/afpd functions only when it is really needed, mainly while
17  * returning some non-UNIX information or filtering by non-UNIX criteria.
18  *
19  * Initial version written by Rafal Lewczuk <rlewczuk@pronet.pl>
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <time.h>
33
34 #if STDC_HEADERS
35 #include <string.h>
36 #else
37 #ifndef HAVE_MEMCPY
38 #define memcpy(d,s,n) bcopy ((s), (d), (n))
39 #define memmove(d,s,n) bcopy ((s), (d), (n))
40 #endif /* ! HAVE_MEMCPY */
41 #endif
42
43 #include <sys/file.h>
44 #include <netinet/in.h>
45
46 #include <atalk/afp.h>
47 #include <atalk/adouble.h>
48 #include <atalk/logger.h>
49 #ifdef CNID_DB
50 #include <atalk/cnid.h>
51 #endif /* CNID_DB */
52
53 #include <atalk/util.h>
54 #include <atalk/bstradd.h>
55
56 #include "desktop.h"
57 #include "directory.h"
58 #include "dircache.h"
59 #include "file.h"
60 #include "volume.h"
61 #include "globals.h"
62 #include "filedir.h"
63 #include "fork.h"
64
65
66 struct finderinfo {
67         u_int32_t f_type;
68         u_int32_t creator;
69         u_int16_t attrs;    /* File attributes (high 8 bits)*/
70         u_int16_t label;    /* Label (low 8 bits )*/
71         char reserved[22]; /* Unknown (at least for now...) */
72 };
73
74 typedef char packed_finder[ADEDLEN_FINDERI];
75
76 /* Known attributes:
77  * 0x04 - has a custom icon
78  * 0x20 - name/icon is locked
79  * 0x40 - is invisible
80  * 0x80 - is alias
81  *
82  * Known labels:
83  * 0x02 - project 2
84  * 0x04 - project 1
85  * 0x06 - personal
86  * 0x08 - cool
87  * 0x0a - in progress
88  * 0x0c - hot
89  * 0x0e - essential
90  */
91
92 /* This is our search-criteria structure. */
93 struct scrit {
94         u_int32_t rbitmap;          /* Request bitmap - which values should we check ? */
95         u_int16_t fbitmap, dbitmap; /* file & directory bitmap - which values should we return ? */
96         u_int16_t attr;             /* File attributes */
97         time_t cdate;               /* Creation date */
98         time_t mdate;               /* Last modification date */
99         time_t bdate;               /* Last backup date */
100         u_int32_t pdid;             /* Parent DID */
101     u_int16_t offcnt;           /* Offspring count */
102         struct finderinfo finfo;    /* Finder info */
103         char lname[64];             /* Long name */ 
104         char utf8name[514];         /* UTF8 or UCS2 name */ /* for convert_charset dest_len parameter +2 */
105 };
106
107 /*
108  * Directory tree search is recursive by its nature. But AFP specification
109  * requires FPCatSearch to pause after returning n results and be able to
110  * resume the search later. So we have to do recursive search using flat
111  * (iterative) algorithm and remember all directories to look into in an
112  * stack-like structure. The structure below is one item of directory stack.
113  *
114  */
115 struct dsitem {
116         struct dir *dir; /* Structure describing this directory */
117         int pidx;        /* Parent's dsitem structure index. */
118         int checked;     /* Have we checked this directory ? */
119         int path_len;
120         char *path;      /* absolute UNIX path to this directory */
121 };
122  
123
124 #define DS_BSIZE 128
125 static int save_cidx = -1; /* Saved index of currently scanned directory. */
126
127 static struct dsitem *dstack = NULL; /* Directory stack data... */
128 static int dssize = 0;               /* Directory stack (allocated) size... */
129 static int dsidx = 0;                /* First free item index... */
130
131 static struct scrit c1, c2;          /* search criteria */
132
133 /* Puts new item onto directory stack. */
134 static int addstack(char *uname, struct dir *dir, int pidx)
135 {
136         struct dsitem *ds;
137         size_t         l, u;
138
139         /* check if we have some space on stack... */
140         if (dsidx >= dssize) {
141                 dssize += DS_BSIZE;
142                 dstack = realloc(dstack, dssize * sizeof(struct dsitem));       
143                 if (dstack == NULL)
144                         return -1;
145         }
146
147         /* Put new element. Allocate and copy lname and path. */
148         ds = dstack + dsidx++;
149         ds->dir = dir;
150     dir->d_flags |= DIRF_CACHELOCK;
151         ds->pidx = pidx;
152         ds->checked = 0;
153         if (pidx >= 0) {
154             l = dstack[pidx].path_len;
155             u = strlen(uname) +1;
156             if (!(ds->path = malloc(l + u + 1) ))
157                         return -1;
158                 memcpy(ds->path, dstack[pidx].path, l);
159                 ds->path[l] = '/';
160                 memcpy(&ds->path[l+1], uname, u);
161                 ds->path_len = l +u;
162         }
163         else {
164             ds->path = strdup(uname);
165                 ds->path_len = strlen(uname);
166         }
167         return 0;
168 }
169
170 /* Removes checked items from top of directory stack. Returns index of the first unchecked elements or -1. */
171 static int reducestack(void)
172 {
173         int r;
174         if (save_cidx != -1) {
175                 r = save_cidx;
176                 save_cidx = -1;
177                 return r;
178         }
179
180         while (dsidx > 0) {
181                 if (dstack[dsidx-1].checked) {
182                         dsidx--;
183             dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK;
184                         free(dstack[dsidx].path);
185                 } else
186                         return dsidx - 1;
187         } 
188         return -1;
189
190
191 /* Clears directory stack. */
192 static void clearstack(void) 
193 {
194         save_cidx = -1;
195         while (dsidx > 0) {
196                 dsidx--;
197         dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK;
198                 free(dstack[dsidx].path);
199         }
200
201
202 /* Looks up for an opened adouble structure, opens resource fork of selected file. 
203  * FIXME What about noadouble?
204 */
205 static struct adouble *adl_lkup(struct vol *vol, struct path *path, struct adouble *adp)
206 {
207         static struct adouble ad;
208         
209         struct ofork *of;
210         int isdir;
211         
212         if (adp)
213             return adp;
214             
215         isdir  = S_ISDIR(path->st.st_mode);
216
217         if (!isdir && (of = of_findname(path))) {
218                 adp = of->of_ad;
219         } else {
220                 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
221                 adp = &ad;
222         } 
223
224     if ( ad_metadata( path->u_name, ((isdir) ? ADFLAGS_DIR : 0), adp) < 0 ) {
225         adp = NULL; /* FIXME without resource fork adl_lkup will be call again */
226     }
227     
228         return adp;     
229 }
230
231 /* -------------------- */
232 static struct finderinfo *unpack_buffer(struct finderinfo *finfo, char *buffer)
233 {
234         memcpy(&finfo->f_type,  buffer +FINDERINFO_FRTYPEOFF, sizeof(finfo->f_type));
235         memcpy(&finfo->creator, buffer +FINDERINFO_FRCREATOFF, sizeof(finfo->creator));
236         memcpy(&finfo->attrs,   buffer +FINDERINFO_FRFLAGOFF, sizeof(finfo->attrs));
237         memcpy(&finfo->label,   buffer +FINDERINFO_FRFLAGOFF, sizeof(finfo->label));
238         finfo->attrs &= 0xff00; /* high 8 bits */
239         finfo->label &= 0xff;   /* low 8 bits */
240
241         return finfo;
242 }
243
244 /* -------------------- */
245 static struct finderinfo *
246 unpack_finderinfo(struct vol *vol, struct path *path, struct adouble **adp, struct finderinfo *finfo, int islnk)
247 {
248         packed_finder  buf;
249         void           *ptr;
250         
251     *adp = adl_lkup(vol, path, *adp);
252         ptr = get_finderinfo(vol, path->u_name, *adp, &buf,islnk);
253         return unpack_buffer(finfo, ptr);
254 }
255
256 /* -------------------- */
257 #define CATPBIT_PARTIAL 31
258 /* Criteria checker. This function returns a 2-bit value. */
259 /* bit 0 means if checked file meets given criteria. */
260 /* bit 1 means if it is a directory and we should descent into it. */
261 /* uname - UNIX name 
262  * fname - our fname (translated to UNIX)
263  * cidx - index in directory stack
264  */
265 static int crit_check(struct vol *vol, struct path *path) {
266         int result = 0;
267         u_int16_t attr, flags = CONV_PRECOMPOSE;
268         struct finderinfo *finfo = NULL, finderinfo;
269         struct adouble *adp = NULL;
270         time_t c_date, b_date;
271         u_int32_t ac_date, ab_date;
272         static char convbuf[514]; /* for convert_charset dest_len parameter +2 */
273         size_t len;
274     int islnk;
275     islnk=S_ISLNK(path->st.st_mode);
276
277         if (S_ISDIR(path->st.st_mode)) {
278                 if (!c1.dbitmap)
279                         return 0;
280         }
281         else {
282                 if (!c1.fbitmap)
283                         return 0;
284
285                 /* compute the Mac name 
286                  * first try without id (it's slow to find it)
287                  * An other option would be to call get_id in utompath but 
288                  * we need to pass parent dir
289                 */
290         if (!(path->m_name = utompath(vol, path->u_name, 0 , utf8_encoding()) )) {
291                 /*retry with the right id */
292        
293                 cnid_t id;
294                 
295                 adp = adl_lkup(vol, path, adp);
296                 id = get_id(vol, adp, &path->st, path->d_dir->d_did, path->u_name, strlen(path->u_name));
297                 if (!id) {
298                         /* FIXME */
299                         return 0;
300                 }
301                 /* save the id for getfilparm */
302                 path->id = id;
303                 if (!(path->m_name = utompath(vol, path->u_name, id , utf8_encoding()))) {
304                         return 0;
305                 }
306         }
307         }
308                 
309         /* Kind of optimization: 
310          * -- first check things we've already have - filename
311          * -- last check things we get from ad_open()
312          * FIXME strmcp strstr (icase)
313          */
314
315         /* Check for filename */
316         if ((c1.rbitmap & (1<<DIRPBIT_LNAME))) { 
317                 if ( (size_t)(-1) == (len = convert_string(vol->v_maccharset, CH_UCS2, path->m_name, -1, convbuf, 512)) )
318                         goto crit_check_ret;
319
320                 if ((c1.rbitmap & (1<<CATPBIT_PARTIAL))) {
321                         if (strcasestr_w( (ucs2_t*) convbuf, (ucs2_t*) c1.lname) == NULL)
322                                 goto crit_check_ret;
323                 } else
324                         if (strcasecmp_w((ucs2_t*) convbuf, (ucs2_t*) c1.lname) != 0)
325                                 goto crit_check_ret;
326         } 
327         
328         if ((c1.rbitmap & (1<<FILPBIT_PDINFO))) { 
329                 if ( (size_t)(-1) == (len = convert_charset( CH_UTF8_MAC, CH_UCS2, CH_UTF8, path->m_name, strlen(path->m_name), convbuf, 512, &flags))) {
330                         goto crit_check_ret;
331                 }
332
333                 if (c1.rbitmap & (1<<CATPBIT_PARTIAL)) {
334                         if (strcasestr_w((ucs2_t *) convbuf, (ucs2_t*)c1.utf8name) == NULL)
335                                 goto crit_check_ret;
336                 } else
337                         if (strcasecmp_w((ucs2_t *)convbuf, (ucs2_t*)c1.utf8name) != 0)
338                                 goto crit_check_ret;
339         } 
340
341
342         /* FIXME */
343         if ((unsigned)c2.mdate > 0x7fffffff)
344                 c2.mdate = 0x7fffffff;
345         if ((unsigned)c2.cdate > 0x7fffffff)
346                 c2.cdate = 0x7fffffff;
347         if ((unsigned)c2.bdate > 0x7fffffff)
348                 c2.bdate = 0x7fffffff;
349
350         /* Check for modification date */
351         if ((c1.rbitmap & (1<<DIRPBIT_MDATE))) {
352                 if (path->st.st_mtime < c1.mdate || path->st.st_mtime > c2.mdate)
353                         goto crit_check_ret;
354         }
355         
356         /* Check for creation date... */
357         if ((c1.rbitmap & (1<<DIRPBIT_CDATE))) {
358                 c_date = path->st.st_mtime;
359                 adp = adl_lkup(vol, path, adp);
360                 if (adp && ad_getdate(adp, AD_DATE_CREATE, &ac_date) >= 0)
361                     c_date = AD_DATE_TO_UNIX(ac_date);
362
363                 if (c_date < c1.cdate || c_date > c2.cdate)
364                         goto crit_check_ret;
365         }
366
367         /* Check for backup date... */
368         if ((c1.rbitmap & (1<<DIRPBIT_BDATE))) {
369                 b_date = path->st.st_mtime;
370                 adp = adl_lkup(vol, path, adp);
371                 if (adp && ad_getdate(adp, AD_DATE_BACKUP, &ab_date) >= 0)
372                         b_date = AD_DATE_TO_UNIX(ab_date);
373
374                 if (b_date < c1.bdate || b_date > c2.bdate)
375                         goto crit_check_ret;
376         }
377                                 
378         /* Check attributes */
379         if ((c1.rbitmap & (1<<DIRPBIT_ATTR)) && c2.attr != 0) {
380                 if ((adp = adl_lkup(vol, path, adp))) {
381                         ad_getattr(adp, &attr);
382                         if ((attr & c2.attr) != c1.attr)
383                                 goto crit_check_ret;
384                 } else 
385                         goto crit_check_ret;
386         }               
387
388         /* Check file type ID */
389         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.f_type != 0) {
390             finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
391                 if (finfo->f_type != c1.finfo.f_type)
392                         goto crit_check_ret;
393         }
394         
395         /* Check creator ID */
396         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.creator != 0) {
397                 if (!finfo) {
398                         finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
399                 }
400                 if (finfo->creator != c1.finfo.creator)
401                         goto crit_check_ret;
402         }
403                 
404         /* Check finder info attributes */
405         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.attrs != 0) {
406                 if (!finfo) {
407                         finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
408                 }
409
410                 if ((finfo->attrs & c2.finfo.attrs) != c1.finfo.attrs)
411                         goto crit_check_ret;
412         }
413         
414         /* Check label */
415         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.label != 0) {
416                 if (!finfo) {
417                         finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
418                 }
419                 if ((finfo->label & c2.finfo.label) != c1.finfo.label)
420                         goto crit_check_ret;
421         }       
422         /* FIXME: Attributes check ! */
423         
424         /* All criteria are met. */
425         result |= 1;
426 crit_check_ret:
427         if (adp != NULL)
428                 ad_close_metadata(adp);
429         return result;
430 }  
431
432 /* ------------------------------ */
433 static int rslt_add ( struct vol *vol, struct path *path, char **buf, int ext)
434 {
435
436         char            *p = *buf;
437         int             ret;
438         size_t          tbuf =0;
439         u_int16_t       resultsize;
440         int             isdir = S_ISDIR(path->st.st_mode); 
441
442         /* Skip resultsize */
443         if (ext) {
444                 p += sizeof(resultsize); 
445         }
446         else {
447                 p++;
448         }
449         *p++ = isdir ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;    /* IsDir ? */
450
451         if (ext) {
452                 *p++ = 0;                  /* Pad */
453         }
454         
455         if ( isdir ) {
456         ret = getdirparams(vol, c1.dbitmap, path, path->d_dir, p , &tbuf ); 
457         }
458         else {
459             /* FIXME slow if we need the file ID, we already know it, done ? */
460                 ret = getfilparams ( vol, c1.fbitmap, path, path->d_dir, p, &tbuf);
461         }
462
463         if ( ret != AFP_OK )
464                 return 0;
465
466         /* Make sure entry length is even */
467         if ((tbuf & 1)) {
468            *p++ = 0;
469            tbuf++;
470         }
471
472         if (ext) {
473                 resultsize = htons(tbuf);
474                 memcpy ( *buf, &resultsize, sizeof(resultsize) );
475                 *buf += tbuf + 4;
476         }
477         else {
478                 **buf = tbuf;
479                 *buf += tbuf + 2;
480         }
481
482         return 1;
483
484         
485 #define VETO_STR \
486         "./../.AppleDouble/.AppleDB/Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/.AppleDesktop/.Parent/"
487
488 /* This function performs search. It is called directly from afp_catsearch 
489  * vol - volume we are searching on ...
490  * dir - directory we are starting from ...
491  * c1, c2 - search criteria
492  * rmatches - maximum number of matches we can return
493  * pos - position we've stopped recently
494  * rbuf - output buffer
495  * rbuflen - output buffer length
496  */
497 #define NUM_ROUNDS 200
498 static int catsearch(struct vol *vol, struct dir *dir,  
499                      int rmatches, u_int32_t *pos, char *rbuf, u_int32_t *nrecs, int *rsize, int ext)
500 {
501     static u_int32_t cur_pos;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
502     static DIR *dirpos;                  /* UNIX structure describing currently opened directory. */
503         int cidx, r;
504         struct dirent *entry;
505         int result = AFP_OK;
506         int ccr;
507     struct path path;
508         char *vpath = vol->v_path;
509         char *rrbuf = rbuf;
510     time_t start_time;
511     int num_rounds = NUM_ROUNDS;
512     int cwd = -1;
513     int error;
514         
515         if (*pos != 0 && *pos != cur_pos) {
516                 result = AFPERR_CATCHNG;
517                 goto catsearch_end;
518         }
519
520         /* FIXME: Category "offspring count ! */
521
522
523         /* We need to initialize all mandatory structures/variables and change working directory appropriate... */
524         if (*pos == 0) {
525                 clearstack();
526                 if (dirpos != NULL) {
527                         closedir(dirpos);
528                         dirpos = NULL;
529                 } 
530                 
531                 if (addstack(vpath, dir, -1) == -1) {
532                         result = AFPERR_MISC;
533                         goto catsearch_end;
534                 }
535                 /* FIXME: Sometimes DID is given by client ! (correct this one above !) */
536         }
537
538         /* Save current path */
539     if ((cwd = open(".", O_RDONLY)) < 0) {
540         result = AFPERR_MISC;
541         goto catsearch_end;
542     }
543         
544         /* So we are beginning... */
545     start_time = time(NULL);
546
547         while ((cidx = reducestack()) != -1) {
548                 error = lchdir(dstack[cidx].path);
549
550                 if (!error && dirpos == NULL)
551                         dirpos = opendir(".");
552
553                 if (dirpos == NULL)
554                         dirpos = opendir(dstack[cidx].path);
555
556                 if (error || dirpos == NULL) {
557                         switch (errno) {
558                         case EACCES:
559                                 dstack[cidx].checked = 1;
560                                 continue;
561                         case EMFILE:
562                         case ENFILE:
563                         case ENOENT:
564                                 result = AFPERR_NFILE;
565                                 break;
566                         case ENOMEM:
567                         case ENOTDIR:
568                         default:
569                                 result = AFPERR_MISC;
570                         } /* switch (errno) */
571                         goto catsearch_end;
572                 }
573                 
574                 while ((entry=readdir(dirpos)) != NULL) {
575                         (*pos)++;
576
577                         if (!check_dirent(vol, entry->d_name))
578                            continue;
579
580                         memset(&path, 0, sizeof(path));
581                         path.u_name = entry->d_name;
582                         if (of_stat(&path) != 0) {
583                                 switch (errno) {
584                                 case EACCES:
585                                 case ELOOP:
586                                 case ENOENT:
587                                         continue;
588                                 case ENOTDIR:
589                                 case EFAULT:
590                                 case ENOMEM:
591                                 case ENAMETOOLONG:
592                                 default:
593                                         result = AFPERR_MISC;
594                                         goto catsearch_end;
595                                 } 
596                         }
597                         if (S_ISDIR(path.st.st_mode)) {
598                                 /* here we can short cut 
599                                    ie if in the same loop the parent dir wasn't in the cache
600                                    ALL dirsearch_byname will fail.
601                                 */
602                 int unlen = strlen(path.u_name);
603                 path.d_dir = dircache_search_by_name(vol, dstack[cidx].dir, path.u_name, unlen);
604                 if (path.d_dir == NULL) {
605                         /* path.m_name is set by adddir */
606                     if (NULL == (path.d_dir = dir_add( vol, dstack[cidx].dir, &path, unlen) ) ) {
607                                                 result = AFPERR_MISC;
608                                                 goto catsearch_end;
609                                         }
610                 }
611                 path.m_name = cfrombstring(path.d_dir->d_m_name);
612                         
613                                 if (addstack(path.u_name, path.d_dir, cidx) == -1) {
614                                         result = AFPERR_MISC;
615                                         goto catsearch_end;
616                                 } 
617             }
618             else {
619                 /* yes it sucks for directory d_dir is the directory, for file it's the parent directory*/
620                 path.d_dir = dstack[cidx].dir;
621             }
622                         ccr = crit_check(vol, &path);
623
624                         /* bit 0 means that criteria has been met */
625                         if ((ccr & 1)) {
626                                 r = rslt_add ( vol, &path, &rrbuf, ext);
627                                 
628                                 if (r == 0) {
629                                         result = AFPERR_MISC;
630                                         goto catsearch_end;
631                                 } 
632                                 *nrecs += r;
633                                 /* Number of matches limit */
634                                 if (--rmatches == 0) 
635                                         goto catsearch_pause;
636                                 /* Block size limit */
637                                 if (rrbuf - rbuf >= 448)
638                                         goto catsearch_pause;
639                         }
640                         /* MacOS 9 doesn't like servers executing commands longer than few seconds */
641                         if (--num_rounds <= 0) {
642                             if (start_time != time(NULL)) {
643                                         result=AFP_OK;
644                                         goto catsearch_pause;
645                             }
646                             num_rounds = NUM_ROUNDS;
647                         }
648                 } /* while ((entry=readdir(dirpos)) != NULL) */
649                 closedir(dirpos);
650                 dirpos = NULL;
651                 dstack[cidx].checked = 1;
652         } /* while (current_idx = reducestack()) != -1) */
653
654         /* We have finished traversing our tree. Return EOF here. */
655         result = AFPERR_EOF;
656         goto catsearch_end;
657
658 catsearch_pause:
659         cur_pos = *pos; 
660         save_cidx = cidx;
661
662 catsearch_end: /* Exiting catsearch: error condition */
663         *rsize = rrbuf - rbuf;
664     if (cwd != -1) {
665         if ((fchdir(cwd)) != 0) {
666             LOG(log_debug, logtype_afpd, "error chdiring back: %s", strerror(errno));        
667         }
668         close(cwd);
669     }
670         return result;
671 } /* catsearch() */
672
673 /* -------------------------- */
674 static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
675                   char *rbuf, size_t *rbuflen, int ext)
676 {
677     struct vol *vol;
678     u_int16_t   vid;
679     u_int16_t   spec_len;
680     u_int32_t   rmatches, reserved;
681     u_int32_t   catpos[4];
682     u_int32_t   pdid = 0;
683     int ret, rsize;
684     u_int32_t nrecs = 0;
685     unsigned char *spec1, *spec2, *bspec1, *bspec2;
686     size_t      len;
687     u_int16_t   namelen;
688     u_int16_t   flags;
689     char        tmppath[256];
690
691     *rbuflen = 0;
692
693     /* min header size */
694     if (ibuflen < 32) {
695         return AFPERR_PARAM;
696     }
697
698     memset(&c1, 0, sizeof(c1));
699     memset(&c2, 0, sizeof(c2));
700
701     ibuf += 2;
702     memcpy(&vid, ibuf, sizeof(vid));
703     ibuf += sizeof(vid);
704
705     if ((vol = getvolbyvid(vid)) == NULL) {
706         return AFPERR_PARAM;
707     }
708     
709     memcpy(&rmatches, ibuf, sizeof(rmatches));
710     rmatches = ntohl(rmatches);
711     ibuf += sizeof(rmatches); 
712
713     /* FIXME: (rl) should we check if reserved == 0 ? */
714     ibuf += sizeof(reserved);
715
716     memcpy(catpos, ibuf, sizeof(catpos));
717     ibuf += sizeof(catpos);
718
719     memcpy(&c1.fbitmap, ibuf, sizeof(c1.fbitmap));
720     c1.fbitmap = c2.fbitmap = ntohs(c1.fbitmap);
721     ibuf += sizeof(c1.fbitmap);
722
723     memcpy(&c1.dbitmap, ibuf, sizeof(c1.dbitmap));
724     c1.dbitmap = c2.dbitmap = ntohs(c1.dbitmap);
725     ibuf += sizeof(c1.dbitmap);
726
727     memcpy(&c1.rbitmap, ibuf, sizeof(c1.rbitmap));
728     c1.rbitmap = c2.rbitmap = ntohl(c1.rbitmap);
729     ibuf += sizeof(c1.rbitmap);
730
731     if (! (c1.fbitmap || c1.dbitmap)) {
732             return AFPERR_BITMAP;
733     }
734
735     if ( ext) {
736         memcpy(&spec_len, ibuf, sizeof(spec_len));
737         spec_len = ntohs(spec_len);
738     }
739     else {
740         /* with catsearch only name and parent id are allowed */
741         c1.fbitmap &= (1<<FILPBIT_LNAME) | (1<<FILPBIT_PDID);
742         c1.dbitmap &= (1<<DIRPBIT_LNAME) | (1<<DIRPBIT_PDID);
743         spec_len = *(unsigned char*)ibuf;
744     }
745
746     /* Parse file specifications */
747     spec1 = (unsigned char*)ibuf;
748     spec2 = (unsigned char*)ibuf + spec_len + 2;
749
750     spec1 += 2; 
751     spec2 += 2; 
752
753     bspec1 = spec1;
754     bspec2 = spec2;
755     /* File attribute bits... */
756     if (c1.rbitmap & (1 << FILPBIT_ATTR)) {
757             memcpy(&c1.attr, spec1, sizeof(c1.attr));
758             spec1 += sizeof(c1.attr);
759             memcpy(&c2.attr, spec2, sizeof(c2.attr));
760             spec2 += sizeof(c1.attr);
761     }
762
763     /* Parent DID */
764     if (c1.rbitmap & (1 << FILPBIT_PDID)) {
765             memcpy(&c1.pdid, spec1, sizeof(pdid));
766             spec1 += sizeof(c1.pdid);
767             memcpy(&c2.pdid, spec2, sizeof(pdid));
768             spec2 += sizeof(c2.pdid);
769     } /* FIXME: PDID - do we demarshall this argument ? */
770
771     /* Creation date */
772     if (c1.rbitmap & (1 << FILPBIT_CDATE)) {
773             memcpy(&c1.cdate, spec1, sizeof(c1.cdate));
774             spec1 += sizeof(c1.cdate);
775             c1.cdate = AD_DATE_TO_UNIX(c1.cdate);
776             memcpy(&c2.cdate, spec2, sizeof(c2.cdate));
777             spec2 += sizeof(c1.cdate);
778             ibuf += sizeof(c1.cdate);;
779             c2.cdate = AD_DATE_TO_UNIX(c2.cdate);
780     }
781
782     /* Modification date */
783     if (c1.rbitmap & (1 << FILPBIT_MDATE)) {
784             memcpy(&c1.mdate, spec1, sizeof(c1.mdate));
785             c1.mdate = AD_DATE_TO_UNIX(c1.mdate);
786             spec1 += sizeof(c1.mdate);
787             memcpy(&c2.mdate, spec2, sizeof(c2.mdate));
788             c2.mdate = AD_DATE_TO_UNIX(c2.mdate);
789             spec2 += sizeof(c1.mdate);
790     }
791     
792     /* Backup date */
793     if (c1.rbitmap & (1 << FILPBIT_BDATE)) {
794             memcpy(&c1.bdate, spec1, sizeof(c1.bdate));
795             spec1 += sizeof(c1.bdate);
796             c1.bdate = AD_DATE_TO_UNIX(c1.bdate);
797             memcpy(&c2.bdate, spec2, sizeof(c2.bdate));
798             spec2 += sizeof(c2.bdate);
799             c1.bdate = AD_DATE_TO_UNIX(c2.bdate);
800     }
801
802     /* Finder info */
803     if (c1.rbitmap & (1 << FILPBIT_FINFO)) {
804         packed_finder buf;
805         
806             memcpy(buf, spec1, sizeof(buf));
807             unpack_buffer(&c1.finfo, buf);      
808             spec1 += sizeof(buf);
809
810             memcpy(buf, spec2, sizeof(buf));
811             unpack_buffer(&c2.finfo, buf);
812             spec2 += sizeof(buf);
813     } /* Finder info */
814
815     if ((c1.rbitmap & (1 << DIRPBIT_OFFCNT)) != 0) {
816         /* Offspring count - only directories */
817                 if (c1.fbitmap == 0) {
818                 memcpy(&c1.offcnt, spec1, sizeof(c1.offcnt));
819                 spec1 += sizeof(c1.offcnt);
820                 c1.offcnt = ntohs(c1.offcnt);
821                 memcpy(&c2.offcnt, spec2, sizeof(c2.offcnt));
822                 spec2 += sizeof(c2.offcnt);
823                 c2.offcnt = ntohs(c2.offcnt);
824                 }
825                 else if (c1.dbitmap == 0) {
826                         /* ressource fork length */
827                 }
828                 else {
829                 return AFPERR_BITMAP;  /* error */
830                 }
831     } /* Offspring count/ressource fork length */
832
833     /* Long name */
834     if (c1.rbitmap & (1 << FILPBIT_LNAME)) {
835         /* Get the long filename */     
836                 memcpy(tmppath, bspec1 + spec1[1] + 1, (bspec1 + spec1[1])[0]);
837                 tmppath[(bspec1 + spec1[1])[0]]= 0;
838                 len = convert_string ( vol->v_maccharset, CH_UCS2, tmppath, -1, c1.lname, sizeof(c1.lname));
839         if (len == (size_t)(-1))
840             return AFPERR_PARAM;
841
842 #if 0   
843                 /* FIXME: do we need it ? It's always null ! */
844                 memcpy(c2.lname, bspec2 + spec2[1] + 1, (bspec2 + spec2[1])[0]);
845                 c2.lname[(bspec2 + spec2[1])[0]]= 0;
846 #endif
847     }
848         /* UTF8 Name */
849     if (c1.rbitmap & (1 << FILPBIT_PDINFO)) {
850
851                 /* offset */
852                 memcpy(&namelen, spec1, sizeof(namelen));
853                 namelen = ntohs (namelen);
854
855                 spec1 = bspec1+namelen+4; /* Skip Unicode Hint */
856
857                 /* length */
858                 memcpy(&namelen, spec1, sizeof(namelen));
859                 namelen = ntohs (namelen);
860                 if (namelen > 255)  /* Safeguard */
861                         namelen = 255;
862
863                 memcpy (c1.utf8name, spec1+2, namelen);
864                 c1.utf8name[(namelen+1)] =0;
865
866                 /* convert charset */
867                 flags = CONV_PRECOMPOSE;
868                 len = convert_charset(CH_UTF8_MAC, CH_UCS2, CH_UTF8, c1.utf8name, namelen, c1.utf8name, 512, &flags);
869         if (len == (size_t)(-1))
870             return AFPERR_PARAM;
871     }
872     
873     /* Call search */
874     *rbuflen = 24;
875     ret = catsearch(vol, vol->v_root, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
876     memcpy(rbuf, catpos, sizeof(catpos));
877     rbuf += sizeof(catpos);
878
879     c1.fbitmap = htons(c1.fbitmap);
880     memcpy(rbuf, &c1.fbitmap, sizeof(c1.fbitmap));
881     rbuf += sizeof(c1.fbitmap);
882
883     c1.dbitmap = htons(c1.dbitmap);
884     memcpy(rbuf, &c1.dbitmap, sizeof(c1.dbitmap));
885     rbuf += sizeof(c1.dbitmap);
886
887     nrecs = htonl(nrecs);
888     memcpy(rbuf, &nrecs, sizeof(nrecs));
889     rbuf += sizeof(nrecs);
890     *rbuflen += rsize;
891
892     return ret;
893 } /* catsearch_afp */
894
895 /* -------------------------- */
896 int afp_catsearch (AFPObj *obj, char *ibuf, size_t ibuflen,
897                   char *rbuf, size_t *rbuflen)
898 {
899         return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 0);
900 }
901
902
903 int afp_catsearch_ext (AFPObj *obj, char *ibuf, size_t ibuflen,
904                   char *rbuf, size_t *rbuflen)
905 {
906         return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 1);
907 }
908
909 /* FIXME: we need a clean separation between afp stubs and 'real' implementation */
910 /* (so, all buffer packing/unpacking should be done in stub, everything else 
911    should be done in other functions) */