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