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