]> arthur.barton.de Git - netatalk.git/blob - bin/megatron/macbin.c
Use correct member of forklen array, from Olaf Hering
[netatalk.git] / bin / megatron / macbin.c
1 /*
2  * $Id: macbin.c,v 1.12 2005-04-28 20:49:19 bfernhomberg Exp $
3  */
4
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif /* HAVE_CONFIG_H */
8
9 #include <sys/types.h>
10 #include <sys/uio.h>
11 #include <sys/time.h>
12 #include <sys/param.h>
13 #ifdef HAVE_FCNTL_H
14 #include <fcntl.h>
15 #endif /* HAVE_FCNTL_H */
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif /* HAVE_UNISTD_H */
19 #include <string.h>
20 #include <strings.h>
21 #include <ctype.h>
22 #include <stdio.h>
23 #include <time.h>
24
25 #include <atalk/adouble.h>
26 #include <netatalk/endian.h>
27 #include "megatron.h"
28 #include "macbin.h"
29
30 /* This allows megatron to generate .bin files that won't choke other
31    well-known converter apps. It also makes sure that checksums
32    always match. (RLB) */
33 #define MACBINARY_PLAY_NICE_WITH_OTHERS
34
35 /*      String used to indicate standard input instead of a disk
36         file.  Should be a string not normally used for a file
37  */
38 #ifndef STDIN
39 #       define  STDIN   "-"
40 #endif /* STDIN */
41
42 /*      Yes and no
43  */
44 #define NOWAY           0
45 #define SURETHANG       1
46
47 /*      Size of a macbinary file header
48  */
49 #define HEADBUFSIZ      128
50
51 /*      Both input and output routines use this struct and the
52         following globals; therefore this module can only be used
53         for one of the two functions at a time.
54  */
55 struct bin_file_data {
56     u_int32_t           forklen[ NUMFORKS ];
57     char                path[ MAXPATHLEN + 1];
58     int                 filed;
59     u_short             headercrc;
60     time_t              gmtoff; /* to convert from/to localtime */
61 }               bin;
62
63 extern char     *forkname[];
64 u_char          head_buf[HEADBUFSIZ];
65
66 /* 
67  * bin_open must be called first.  pass it a filename that is supposed
68  * to contain a macbinary file.  an bin struct will be allocated and
69  * somewhat initialized; bin_filed is set.
70  */
71
72 int bin_open( binfile, flags, fh, options )
73     char                *binfile;
74     int                 flags, options;
75     struct FHeader      *fh;
76 {
77     int                 maxlen;
78     int                 rc;
79     time_t              t;
80     struct tm           *tp;
81
82 #if DEBUG
83     fprintf( stderr, "entering bin_open\n" );
84 #endif /* DEBUG */
85
86     /* call localtime so that we get the timezone offset */
87     bin.gmtoff = 0;
88 #ifndef NO_STRUCT_TM_GMTOFF
89     time(&t);
90     tp = localtime(&t);
91     if (tp)
92         bin.gmtoff = tp->tm_gmtoff;
93 #endif /* ! NO_STRUCT_TM_GMTOFF */
94
95     if ( flags == O_RDONLY ) { /* input */
96         if ( strcmp( binfile, STDIN ) == 0 ) {
97             bin.filed = fileno( stdin );
98         } else if (( bin.filed = open( binfile, flags )) < 0 ) {
99             perror( binfile );
100             return( -1 );
101         }
102 #if DEBUG
103         fprintf( stderr, "opened %s for read\n", binfile );
104 #endif /* DEBUG */
105         if ((( rc = test_header() ) > 0 ) && 
106                 ( bin_header_read( fh, rc ) == 0 )) {
107             return( 0 );
108         }
109         fprintf( stderr, "%s is not a macbinary file.\n", binfile );
110         return( -1 );
111     } else { /* output */
112         if (options & OPTION_STDOUT) 
113           bin.filed = fileno(stdout);
114         else {
115           maxlen = sizeof( bin.path ) - 1;
116 #if DEBUG
117           fprintf( stderr, "sizeof bin.path\t\t\t%d\n", sizeof( bin.path ));
118           fprintf( stderr, "maxlen \t\t\t\t%d\n", maxlen );
119 #endif /* DEBUG */
120           strncpy( bin.path, fh->name, maxlen );
121           strncpy( bin.path, mtoupath( bin.path ), maxlen );
122           strncat( bin.path, ".bin", maxlen - strlen( bin.path ));
123           if (( bin.filed = open( bin.path, flags, 0666 )) < 0 ) {
124             perror( bin.path );
125             return( -1 );
126           }
127 #if DEBUG
128           fprintf( stderr, "opened %s for write\n", 
129                    (options & OPTION_STDOUT) ? "(stdout)" : bin.path );
130 #endif /* DEBUG */
131         }
132
133         if ( bin_header_write( fh ) != 0 ) {
134             bin_close( TRASH );
135             fprintf( stderr, "%s\n", bin.path );
136             return( -1 );
137         }
138         return( 0 );
139     }
140 }
141
142 /* 
143  * bin_close must be called before a second file can be opened using
144  * bin_open.  Upon successful completion, a value of 0 is returned.  
145  * Otherwise, a value of -1 is returned.
146  */
147
148 int bin_close( keepflag )
149     int                 keepflag;
150 {
151 #if DEBUG
152     fprintf( stderr, "entering bin_close\n" );
153 #endif /* DEBUG */
154     if ( keepflag == KEEP ) {
155         return( close( bin.filed ));
156     } else if ( keepflag == TRASH ) {
157         if (( strcmp( bin.path, STDIN ) != 0 ) && 
158                 ( unlink( bin.path ) < 0 )) {
159             perror ( bin.path );
160         }
161         return( 0 );
162     } else return( -1 );
163 }
164
165 /*
166  * bin_read is called until it returns zero for each fork.  when it is
167  * and finds that there is zero left to give, it seeks to the position
168  * of the next fork (if there is one ).
169  * bin_read must be called enough times to
170  * return zero and no more than that.
171  */
172
173 int bin_read( fork, buffer, length )
174     int                 fork;
175     char                *buffer;
176     int                 length;
177 {
178     char                *buf_ptr;
179     int                 readlen;
180     int                 cc = 1;
181     off_t               pos;
182
183 #if DEBUG >= 3
184     fprintf( stderr, "bin_read: fork is %s\n", forkname[ fork ] );
185     fprintf( stderr, "bin_read: remaining length is %d\n", bin.forklen[fork] );
186 #endif /* DEBUG >= 3 */
187
188     if (bin.forklen[fork] > length) {
189         fprintf(stderr, "This should never happen, dude! length %d, fork length == %u\n", length, bin.forklen[fork]);
190         return bin.forklen[fork];
191     }
192
193     if ( bin.forklen[ fork ] == 0 ) {
194         if ( fork == DATA ) {
195             pos = lseek( bin.filed, 0, SEEK_CUR );
196 #if DEBUG
197             fprintf( stderr, "current position is %ld\n", pos );
198 #endif /* DEBUG */
199             pos %= HEADBUFSIZ;
200             if (pos != 0) {
201               pos = lseek( bin.filed, HEADBUFSIZ - pos, SEEK_CUR );
202             }
203 #if DEBUG
204             fprintf( stderr, "current position is %ld\n", pos );
205 #endif /* DEBUG */
206         }
207         return( 0 );
208     }
209
210     if ( bin.forklen[ fork ] < length ) {
211         readlen = bin.forklen[ fork ];
212     } else {
213         readlen = length;
214     }
215 #if DEBUG >= 3
216     fprintf( stderr, "bin_read: readlen is %d\n", readlen );
217     fprintf( stderr, "bin_read: cc is %d\n", cc );
218 #endif /* DEBUG >= 3 */
219
220     buf_ptr = buffer;
221     while (( readlen > 0 ) && ( cc > 0 )) {
222         if (( cc = read( bin.filed, buf_ptr, readlen )) > 0 ) {
223 #if DEBUG >= 3
224             fprintf( stderr, "bin_read: cc is %d\n", cc );
225 #endif /* DEBUG >= 3 */
226             readlen -= cc;
227             buf_ptr += cc;
228         }
229     }
230     if ( cc >= 0 ) {
231         cc = buf_ptr - buffer;
232         bin.forklen[ fork ] -= cc;
233     }
234
235 #if DEBUG >= 3
236     fprintf( stderr, "bin_read: chars read is %d\n", cc );
237 #endif /* DEBUG >= 3 */
238     return( cc );
239 }
240
241 /*
242  * bin_write 
243  */
244
245 int bin_write( fork, buffer, length )
246     int                 fork;
247     char                *buffer;
248     int                 length;
249 {
250     char                *buf_ptr;
251     int                 writelen;
252     int                 cc = 0;
253     off_t               pos;
254     u_char              padchar = 0x7f;
255                 /* Not sure why, but it seems this must be 0x7f to match
256                    other converters, not 0. (RLB) */
257
258 #if DEBUG >= 3
259     fprintf( stderr, "bin_write: fork is %s\n", forkname[ fork ] );
260     fprintf( stderr, "bin_write: remaining length is %d\n", bin.forklen[fork] );
261 #endif /* DEBUG >= 3 */
262
263     if (( fork == RESOURCE ) && ( bin.forklen[ DATA ] != 0 )) {
264         fprintf( stderr, "Forklength error.\n" );
265         return( -1 );
266     }
267
268     buf_ptr = (char *)buffer;
269     if ( bin.forklen[ fork ] >= length ) {
270         writelen = length;
271     } else {
272         fprintf( stderr, "Forklength error.\n" );
273         return( -1 );
274     }
275
276 #if DEBUG >= 3
277     fprintf( stderr, "bin_write: write length is %d\n", writelen );
278 #endif /* DEBUG >= 3 */
279
280     while (( writelen > 0 ) && ( cc >= 0 )) {
281         cc = write( bin.filed, buf_ptr, writelen );
282         buf_ptr += cc;
283         writelen -= cc;
284     }
285     if ( cc < 0 ) {
286         perror( "Couldn't write to macbinary file:" );
287         return( cc );
288     }
289
290     if (length > bin.forklen[fork]) {
291         fprintf(stderr, "This should never happen, dude! length %d, fork length %u\n", length, bin.forklen[fork]);
292         return bin.forklen[fork];
293     }
294
295     bin.forklen[fork] -= length;
296
297 /*
298  * add the padding at end of data and resource forks
299  */
300
301     if ( bin.forklen[ fork ] == 0 ) {
302         pos = lseek( bin.filed, 0, SEEK_CUR );
303 #if DEBUG
304         fprintf( stderr, "current position is %ld\n", pos );
305 #endif /* DEBUG */
306         pos %= HEADBUFSIZ;
307         if (pos != 0) { /* pad only if we need to */
308           pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR );
309           if ( write( bin.filed, &padchar, 1 ) != 1 ) {
310             perror( "Couldn't write to macbinary file:" );
311             return( -1 );
312           }
313         }
314 #if DEBUG
315           fprintf( stderr, "current position is %ld\n", pos );
316 #endif /* DEBUG */
317     }
318
319 #if DEBUG
320         fprintf( stderr, "\n" );
321 #endif /* DEBUG */
322
323     return( length );
324 }
325
326 /* 
327  * bin_header_read is called by bin_open, and before any information can
328  * read from the fh substruct.  it must be called before any
329  * of the bytes of the other two forks can be read, as well.
330  */
331
332 int bin_header_read( fh, revision )
333     struct FHeader      *fh;
334     int                 revision;
335 {
336     u_short             mask;
337
338 /*
339  * Set the appropriate finder flags mask for the type of macbinary
340  * file it is, and copy the extra macbinary II stuff from the header.
341  * If it is not a macbinary file revision of I or II, then return
342  * negative.
343  */
344
345     switch ( revision ) {
346         case 3:
347         case 2 :
348             mask = htons( 0xfcee );
349             memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
350             break;
351         case 1 :
352             mask = htons( 0xfc00 );
353             break;
354         default :
355             return( -1 );
356             break;
357     }
358
359 /*
360  * Go through and copy all the stuff you can get from the 
361  * MacBinary header into the fh struct.  What fun!
362  */
363
364     memcpy(fh->name, head_buf +  2, head_buf[ 1 ] );
365     memcpy(&fh->create_date, head_buf +  91, 4 );
366     fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff;
367     fh->create_date = AD_DATE_FROM_UNIX(fh->create_date);
368     memcpy( &fh->mod_date, head_buf +  95, 4 );
369     fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff;
370     fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date);
371     fh->backup_date = AD_DATE_START;
372     memcpy( &fh->finder_info, head_buf +  65, 8 );
373
374 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
375     memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 );
376     fh->finder_info.fdFlags &= mask;
377 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
378         memcpy( &fh->finder_info.fdFlags, head_buf + 73, 2 );
379 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
380
381     memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 );
382     memcpy(&fh->finder_info.fdFldr, head_buf +  79, 2 );
383     memcpy(&fh->forklen[ DATA ],  head_buf + 83, 4 );
384     bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
385     memcpy(&fh->forklen[ RESOURCE ],  head_buf +  87, 4 );
386     bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
387     fh->comment[0] = '\0';
388
389     if (revision == 3) {
390       fh->finder_xinfo.fdScript = *(head_buf + 106);
391       fh->finder_xinfo.fdXFlags = *(head_buf + 107);
392     }
393
394 #if DEBUG >= 5
395     {
396         short           flags;
397         long            flags_long;
398
399         fprintf( stderr, "Values read by bin_header_read\n" );
400         fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
401         fprintf( stderr, "file name\t\t%s\n", fh->name );
402         fprintf( stderr, "get info comment\t%s\n", fh->comment );
403         fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
404                 &fh->finder_info.fdType );
405         fprintf( stderr, "creator\t\t\t%.*s\n", 
406                 sizeof( fh->finder_info.fdCreator ), 
407                 &fh->finder_info.fdCreator );
408         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
409         flags = ntohs( flags );
410         fprintf( stderr, "flags\t\t\t%x\n", flags );
411
412         /* Show fdLocation too (RLB) */
413         memcpy( &flags_long, &fh->finder_info.fdLocation,
414                 sizeof( flags_long ));
415         flags_long = ntohl( flags_long );
416         fprintf( stderr, "location flags\t\t%lx\n", flags_long );
417
418         fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
419         fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
420         fprintf( stderr, "\n" );
421     }
422 #endif /* DEBUG >= 5 */
423
424     return( 0 );
425 }
426
427 /* 
428  * bin_header_write is called by bin_open, and relies on information
429  * from the fh substruct.  it must be called before any
430  * of the bytes of the other two forks can be written, as well.
431  * bin_header_write and bin_header_read are opposites.
432  */
433
434 int bin_header_write( fh )
435     struct FHeader      *fh;
436 {
437     char                *write_ptr;
438     u_int32_t           t;
439     int                 wc;
440     int                 wr;
441
442     memset(head_buf, 0, sizeof( head_buf ));
443     head_buf[ 1 ] = (u_char)strlen( fh->name );
444     memcpy( head_buf + 2, fh->name, head_buf[ 1 ] );
445     memcpy( head_buf + 65, &fh->finder_info, 8 );
446
447 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
448     memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1 );
449 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
450     memcpy( head_buf + 73, &fh->finder_info.fdFlags, 2 );
451 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
452
453     memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 );
454     memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 );
455     memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 );
456     memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 );
457     t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff;
458     t = MAC_DATE_FROM_UNIX(t);
459     memcpy( head_buf + 91, &t, sizeof(t) );
460     t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff;
461     t = MAC_DATE_FROM_UNIX(t);
462     memcpy( head_buf + 95, &t, sizeof(t) );
463     memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1);
464
465     /* macbinary III */
466     memcpy( head_buf + 102, "mBIN", 4);
467     *(head_buf + 106) = fh->finder_xinfo.fdScript;
468     *(head_buf + 107) = fh->finder_xinfo.fdXFlags;
469     head_buf[ 122 ] = 130;
470
471     head_buf[ 123 ] = 129;
472
473     bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
474     memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
475
476     bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
477     bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
478
479 #if DEBUG >= 5
480     {
481         short   flags;
482         long    flags_long;
483
484         fprintf( stderr, "Values written by bin_header_write\n" );
485         fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
486         fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] );
487         fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] );
488         fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] );
489
490         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
491         flags = ntohs( flags );
492         fprintf( stderr, "flags\t\t\t%x\n", flags );
493
494         /* Show fdLocation too (RLB) */
495         memcpy( &flags_long, &fh->finder_info.fdLocation,
496                 sizeof( flags_long ));
497         flags_long = ntohl( flags_long );
498         fprintf( stderr, "location flags\t\t%ldx\n", flags_long );
499
500         fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
501         fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
502         fprintf( stderr, "\n" );
503     }
504 #endif /* DEBUG >= 5 */
505
506     write_ptr = (char *)head_buf;
507     wc = sizeof( head_buf );
508     wr = 0;
509     while (( wc > 0 ) && ( wr >= 0 )) {
510         wr = write( bin.filed, write_ptr, wc );
511         write_ptr += wr;
512         wc -= wr;
513     }
514     if ( wr < 0 ) {
515         perror( "Couldn't write macbinary header:" );
516         return( wr );
517     }
518
519     return( 0 );
520 }
521
522 /*
523  * test_header is called from bin_open.  it checks certain values of
524  * the first 128 bytes, determines if the file is a MacBinary,
525  * MacBinary II, MacBinary III, or non-MacBinary file, and returns a
526  * one, two, three or negative one to indicate the file type.
527  *
528  * If the signature at 102 is equal to "mBIN," then it's a MacBinary
529  * III file. Bytes 0 and 74 must be zero for the file to be any type
530  * of MacBinary.  If the crc of bytes 0 through 123 equals the value
531  * at offset 124 then it is a MacBinary II.  If not, then if byte 82
532  * is zero, byte 2 is a valid value for a mac filename length (between
533  * one and sixty-three), and bytes 101 through 125 are all zero, then
534  * the file is a MacBinary. 
535  *
536  * NOTE: apple's MacBinary II files have a non-zero value at byte 74.
537  * so, the check for byte 74 isn't very useful.
538  */
539
540 int test_header(void)
541 {
542     const char          zeros[25] = "";
543     u_int32_t           cc;
544     u_short             header_crc;
545     u_char              namelen;
546
547 #if DEBUG
548     fprintf( stderr, "entering test_header\n" );
549 #endif /* DEBUG */
550
551     cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
552     if ( cc < sizeof( head_buf )) {
553         perror( "Premature end of file :" );
554         return( -1 );
555     }
556
557 #if DEBUG
558     fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
559 #endif /* DEBUG */
560
561     /* check for macbinary III header */
562     if (memcmp(head_buf + 102, "mBIN", 4) == 0)
563         return 3;
564
565     /* check for macbinary II even if only one of the bytes is zero */
566     if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
567 #if DEBUG
568       fprintf( stderr, "byte 0 and 74 are both zero\n" );
569 #endif /* DEBUG */
570       bin.headercrc = updcrc( (u_short) 0, head_buf, 124 );
571       memcpy(&header_crc, head_buf + 124, sizeof( header_crc ));
572       header_crc = ntohs( header_crc );
573       if ( header_crc == bin.headercrc ) {
574         return( 2 );
575       }
576
577 #if DEBUG
578       fprintf( stderr, "header crc didn't pan out\n" );
579 #endif /* DEBUG */
580     }
581
582     /* now see if we have a macbinary file. */
583     if ( head_buf[ 82 ] != 0 ) {
584         return( -1 );
585     }
586     memcpy( &namelen, head_buf + 1, sizeof( namelen ));
587 #if DEBUG
588     fprintf( stderr, "name length is %d\n", namelen );
589 #endif /* DEBUG */
590     if (( namelen < 1 ) || ( namelen > 63 )) {
591         return( -1 );
592     }
593
594     /* bytes 101 - 125 should be zero */
595     if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
596         return -1;
597
598     /* macbinary forks aren't larger than 0x7FFFFF */
599     /* we allow forks to be larger, breaking the specs */
600     memcpy(&cc, head_buf + 83, sizeof(cc));
601     cc = ntohl(cc);
602     if (cc > 0x7FFFFFFF)
603         return -1;
604     memcpy(&cc, head_buf + 87, sizeof(cc));
605     cc = ntohl(cc);
606     if (cc > 0x7FFFFFFF)
607         return -1;
608
609
610 #if DEBUG
611     fprintf( stderr, "byte 82 is zero and name length is cool\n" );
612 #endif /* DEBUG */
613
614     return( 1 );
615 }