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