]> arthur.barton.de Git - netatalk.git/blob - bin/megatron/macbin.c
b98ef75720e658fd6d4a819b06c7c32560da3266
[netatalk.git] / bin / megatron / macbin.c
1 /*
2  * $Id: macbin.c,v 1.11 2003-12-15 05:27:24 srittau 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 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
89     time(&t);
90     tp = localtime(&t);
91     if (tp)
92         bin.gmtoff = tp->tm_gmtoff;
93 #endif /* HAVE_STRUCT_TM_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 ] < 0 ) {
189         fprintf( stderr, "This should never happen, dude!\n" );
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     bin.forklen[ fork ] -= length;
290
291     if ( bin.forklen[ fork ] < 0 ) {
292         fprintf( stderr, "This should never happen, dude!\n" );
293         return( bin.forklen[ fork ] );
294     }
295
296 /*
297  * add the padding at end of data and resource forks
298  */
299
300     if ( bin.forklen[ fork ] == 0 ) {
301         pos = lseek( bin.filed, 0, SEEK_CUR );
302 #if DEBUG
303         fprintf( stderr, "current position is %ld\n", pos );
304 #endif /* DEBUG */
305         pos %= HEADBUFSIZ;
306         if (pos != 0) { /* pad only if we need to */
307           pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR );
308           if ( write( bin.filed, &padchar, 1 ) != 1 ) {
309             perror( "Couldn't write to macbinary file:" );
310             return( -1 );
311           }
312         }
313 #if DEBUG
314           fprintf( stderr, "current position is %ld\n", pos );
315 #endif /* DEBUG */
316     }
317
318 #if DEBUG
319         fprintf( stderr, "\n" );
320 #endif /* DEBUG */
321
322     return( length );
323 }
324
325 /* 
326  * bin_header_read is called by bin_open, and before any information can
327  * read from the fh substruct.  it must be called before any
328  * of the bytes of the other two forks can be read, as well.
329  */
330
331 int bin_header_read( fh, revision )
332     struct FHeader      *fh;
333     int                 revision;
334 {
335     u_short             mask;
336
337 /*
338  * Set the appropriate finder flags mask for the type of macbinary
339  * file it is, and copy the extra macbinary II stuff from the header.
340  * If it is not a macbinary file revision of I or II, then return
341  * negative.
342  */
343
344     switch ( revision ) {
345         case 3:
346         case 2 :
347             mask = htons( 0xfcee );
348             memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
349             break;
350         case 1 :
351             mask = htons( 0xfc00 );
352             break;
353         default :
354             return( -1 );
355             break;
356     }
357
358 /*
359  * Go through and copy all the stuff you can get from the 
360  * MacBinary header into the fh struct.  What fun!
361  */
362
363     memcpy(fh->name, head_buf +  2, head_buf[ 1 ] );
364     memcpy(&fh->create_date, head_buf +  91, 4 );
365     fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff;
366     fh->create_date = AD_DATE_FROM_UNIX(fh->create_date);
367     memcpy( &fh->mod_date, head_buf +  95, 4 );
368     fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff;
369     fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date);
370     fh->backup_date = AD_DATE_START;
371     memcpy( &fh->finder_info, head_buf +  65, 8 );
372
373 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
374     memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 );
375     fh->finder_info.fdFlags &= mask;
376 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
377         memcpy( &fh->finder_info.fdFlags, head_buf + 73, 2 );
378 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
379
380     memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 );
381     memcpy(&fh->finder_info.fdFldr, head_buf +  79, 2 );
382     memcpy(&fh->forklen[ DATA ],  head_buf + 83, 4 );
383     bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
384     memcpy(&fh->forklen[ RESOURCE ],  head_buf +  87, 4 );
385     bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
386     fh->comment[0] = '\0';
387
388     if (revision == 3) {
389       fh->finder_xinfo.fdScript = *(head_buf + 106);
390       fh->finder_xinfo.fdXFlags = *(head_buf + 107);
391     }
392
393 #if DEBUG >= 5
394     {
395         short           flags;
396         long            flags_long;
397
398         fprintf( stderr, "Values read by bin_header_read\n" );
399         fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
400         fprintf( stderr, "file name\t\t%s\n", fh->name );
401         fprintf( stderr, "get info comment\t%s\n", fh->comment );
402         fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
403                 &fh->finder_info.fdType );
404         fprintf( stderr, "creator\t\t\t%.*s\n", 
405                 sizeof( fh->finder_info.fdCreator ), 
406                 &fh->finder_info.fdCreator );
407         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
408         flags = ntohs( flags );
409         fprintf( stderr, "flags\t\t\t%x\n", flags );
410
411         /* Show fdLocation too (RLB) */
412         memcpy( &flags_long, &fh->finder_info.fdLocation,
413                 sizeof( flags_long ));
414         flags_long = ntohl( flags_long );
415         fprintf( stderr, "location flags\t\t%lx\n", flags_long );
416
417         fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
418         fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
419         fprintf( stderr, "\n" );
420     }
421 #endif /* DEBUG >= 5 */
422
423     return( 0 );
424 }
425
426 /* 
427  * bin_header_write is called by bin_open, and relies on information
428  * from the fh substruct.  it must be called before any
429  * of the bytes of the other two forks can be written, as well.
430  * bin_header_write and bin_header_read are opposites.
431  */
432
433 int bin_header_write( fh )
434     struct FHeader      *fh;
435 {
436     char                *write_ptr;
437     u_int32_t           t;
438     int                 wc;
439     int                 wr;
440
441     memset(head_buf, 0, sizeof( head_buf ));
442     head_buf[ 1 ] = (u_char)strlen( fh->name );
443     memcpy( head_buf + 2, fh->name, head_buf[ 1 ] );
444     memcpy( head_buf + 65, &fh->finder_info, 8 );
445
446 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
447     memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1 );
448 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
449     memcpy( head_buf + 73, &fh->finder_info.fdFlags, 2 );
450 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
451
452     memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 );
453     memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 );
454     memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 );
455     memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 );
456     t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff;
457     t = MAC_DATE_FROM_UNIX(t);
458     memcpy( head_buf + 91, &t, sizeof(t) );
459     t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff;
460     t = MAC_DATE_FROM_UNIX(t);
461     memcpy( head_buf + 95, &t, sizeof(t) );
462     memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1);
463
464     /* macbinary III */
465     memcpy( head_buf + 102, "mBIN", 4);
466     *(head_buf + 106) = fh->finder_xinfo.fdScript;
467     *(head_buf + 107) = fh->finder_xinfo.fdXFlags;
468     head_buf[ 122 ] = 130;
469
470     head_buf[ 123 ] = 129;
471
472     bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
473     memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
474
475     bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
476     bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
477
478 #if DEBUG >= 5
479     {
480         short   flags;
481         long    flags_long;
482
483         fprintf( stderr, "Values written by bin_header_write\n" );
484         fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
485         fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] );
486         fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] );
487         fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] );
488
489         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
490         flags = ntohs( flags );
491         fprintf( stderr, "flags\t\t\t%x\n", flags );
492
493         /* Show fdLocation too (RLB) */
494         memcpy( &flags_long, &fh->finder_info.fdLocation,
495                 sizeof( flags_long ));
496         flags_long = ntohl( flags_long );
497         fprintf( stderr, "location flags\t\t%ldx\n", flags_long );
498
499         fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
500         fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
501         fprintf( stderr, "\n" );
502     }
503 #endif /* DEBUG >= 5 */
504
505     write_ptr = (char *)head_buf;
506     wc = sizeof( head_buf );
507     wr = 0;
508     while (( wc > 0 ) && ( wr >= 0 )) {
509         wr = write( bin.filed, write_ptr, wc );
510         write_ptr += wr;
511         wc -= wr;
512     }
513     if ( wr < 0 ) {
514         perror( "Couldn't write macbinary header:" );
515         return( wr );
516     }
517
518     return( 0 );
519 }
520
521 /*
522  * test_header is called from bin_open.  it checks certain values of
523  * the first 128 bytes, determines if the file is a MacBinary,
524  * MacBinary II, MacBinary III, or non-MacBinary file, and returns a
525  * one, two, three or negative one to indicate the file type.
526  *
527  * If the signature at 102 is equal to "mBIN," then it's a MacBinary
528  * III file. Bytes 0 and 74 must be zero for the file to be any type
529  * of MacBinary.  If the crc of bytes 0 through 123 equals the value
530  * at offset 124 then it is a MacBinary II.  If not, then if byte 82
531  * is zero, byte 2 is a valid value for a mac filename length (between
532  * one and sixty-three), and bytes 101 through 125 are all zero, then
533  * the file is a MacBinary. 
534  *
535  * NOTE: apple's MacBinary II files have a non-zero value at byte 74.
536  * so, the check for byte 74 isn't very useful.
537  */
538
539 int test_header(void)
540 {
541     const char          zeros[25] = "";
542     u_int32_t           cc;
543     u_short             header_crc;
544     u_char              namelen;
545
546 #if DEBUG
547     fprintf( stderr, "entering test_header\n" );
548 #endif /* DEBUG */
549
550     cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
551     if ( cc < sizeof( head_buf )) {
552         perror( "Premature end of file :" );
553         return( -1 );
554     }
555
556 #if DEBUG
557     fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
558 #endif /* DEBUG */
559
560     /* check for macbinary III header */
561     if (memcmp(head_buf + 102, "mBIN", 4) == 0)
562         return 3;
563
564     /* check for macbinary II even if only one of the bytes is zero */
565     if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
566 #if DEBUG
567       fprintf( stderr, "byte 0 and 74 are both zero\n" );
568 #endif /* DEBUG */
569       bin.headercrc = updcrc( (u_short) 0, head_buf, 124 );
570       memcpy(&header_crc, head_buf + 124, sizeof( header_crc ));
571       header_crc = ntohs( header_crc );
572       if ( header_crc == bin.headercrc ) {
573         return( 2 );
574       }
575
576 #if DEBUG
577       fprintf( stderr, "header crc didn't pan out\n" );
578 #endif /* DEBUG */
579     }
580
581     /* now see if we have a macbinary file. */
582     if ( head_buf[ 82 ] != 0 ) {
583         return( -1 );
584     }
585     memcpy( &namelen, head_buf + 1, sizeof( namelen ));
586 #if DEBUG
587     fprintf( stderr, "name length is %d\n", namelen );
588 #endif /* DEBUG */
589     if (( namelen < 1 ) || ( namelen > 63 )) {
590         return( -1 );
591     }
592
593     /* bytes 101 - 125 should be zero */
594     if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
595         return -1;
596
597     /* macbinary forks aren't larger than 0x7FFFFF */
598     memcpy(&cc, head_buf + 83, sizeof(cc));
599     cc = ntohl(cc);
600     if (cc > 0x7FFFFF)
601         return -1;
602     memcpy(&cc, head_buf + 87, sizeof(cc));
603     cc = ntohl(cc);
604     if (cc > 0x7FFFFF)
605         return -1;
606
607
608 #if DEBUG
609     fprintf( stderr, "byte 82 is zero and name length is cool\n" );
610 #endif /* DEBUG */
611
612     return( 1 );
613 }