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