ole.c 13 KB
Newer Older
1 2 3 4 5 6 7 8 9
/**
 * ole.c: OLE2 file format helper program,
 *        good for dumping OLE streams, and
 * corresponding biff records, and hopefuly
 * some more ...
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 **/
Michael Meeks's avatar
Michael Meeks committed
10 11 12
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
13
#include <ctype.h>
Michael Meeks's avatar
Michael Meeks committed
14 15

#include "ms-ole.h"
16
#include "ms-biff.h"
17
#include "biff-types.h"
18

19 20
#define BIFF_TYPES_FILE    "biff-types.h"
#define ESCHER_TYPES_FILE  "escher-types.h"
21

22 23
char delim[]=" \t\n";

24 25 26
typedef struct {
	guint16 opcode;
	char *name;
27
} GENERIC_TYPE;
28

29 30 31
static GPtrArray *biff_types   = NULL;
static GPtrArray *escher_types = NULL;
typedef enum { eBiff=0, eEscher=1 } typeType;
32 33

static void
34
read_types (char *fname, GPtrArray **types, typeType t)
35
{
36
	FILE *file = fopen(fname, "r");
37
	char buffer[1024];
38
	*types = g_ptr_array_new ();
39
	if (!file) {
40
		printf ("Can't find vital file '%s'\n", fname);
41 42
		return;
	}
43 44 45 46 47
	while (!feof(file)) {
		char *p;
		fgets(buffer,1023,file);
		for (p=buffer;*p;p++)
			if (*p=='0' && *(p+1)=='x') {
48
				GENERIC_TYPE *bt = g_new (GENERIC_TYPE,1);
49 50 51 52 53 54
				char *name, *pt;
				bt->opcode=strtol(p+2,0,16);
				pt = buffer;
				while (*pt && *pt != '#') pt++;      /* # */
				while (*pt && !isspace(*pt)) pt++;  /* define */
				while (*pt &&  isspace(*pt)) pt++;  /* '   ' */
55 56 57 58 59
				if (t==eBiff) {
					while (*pt && *pt != '_') pt++;     /* BIFF_ */
					name = *pt?pt+1:pt;
				} else
					name = pt;
60 61
				while (*pt && !isspace(*pt)) pt++;
				bt->name=g_strndup(name, (pt-name));
62
				g_ptr_array_add (*types, bt);
63 64 65 66 67 68 69
				break;
			}
	}
	fclose (file);
}

static char*
70
get_biff_opcode_name (guint16 opcode)
71 72
{
	int lp;
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
	if (!biff_types)
		read_types (BIFF_TYPES_FILE, &biff_types, eBiff);
	for (lp=0;lp<biff_types->len;lp++) {
		GENERIC_TYPE *bt = g_ptr_array_index (biff_types, lp);
		if (bt->opcode>0xff) {
			if (bt->opcode == opcode)
				return bt->name;
		} else {
			if (bt->opcode == (opcode&0xff))
				return bt->name;
		}
	}
	return "Unknown";
}

static char*
get_escher_opcode_name (guint16 opcode)
{
	int lp;
	if (!escher_types)
		read_types (ESCHER_TYPES_FILE, &escher_types, eEscher);
	for (lp=0;lp<escher_types->len;lp++) {
		GENERIC_TYPE *bt = g_ptr_array_index (escher_types, lp);
96 97 98 99 100 101 102 103 104 105
		if (bt->opcode>0xff) {
			if (bt->opcode == opcode)
				return bt->name;
		} else {
			if (bt->opcode == (opcode&0xff))
				return bt->name;
		}
	}
	return "Unknown";
}
Michael Meeks's avatar
Michael Meeks committed
106

Michael Meeks's avatar
Michael Meeks committed
107 108
static MS_OLE_DIRECTORY *
get_file_handle (MS_OLE *ole, char *name)
Michael Meeks's avatar
Michael Meeks committed
109
{
Michael Meeks's avatar
Michael Meeks committed
110 111 112 113 114
	MS_OLE_DIRECTORY *dir;
	if (!name)
		return NULL;
	dir = ms_ole_directory_new (ole);
	while (ms_ole_directory_next(dir)) {
115 116 117 118
		if (!dir->name) {
			printf ("Odd: NULL dirctory name\n");
			continue;
		}
Michael Meeks's avatar
Michael Meeks committed
119 120 121 122 123 124 125
		if (g_strcasecmp(dir->name, name)==0) {
			return dir;
		}
	}	
	printf ("Stream '%s' not found\n", name);
	ms_ole_directory_destroy (dir);
	return NULL;
Michael Meeks's avatar
Michael Meeks committed
126 127
}

Michael Meeks's avatar
Michael Meeks committed
128 129
static void
list_files (MS_OLE *ole)
Michael Meeks's avatar
Michael Meeks committed
130
{
Michael Meeks's avatar
Michael Meeks committed
131
	MS_OLE_DIRECTORY *dir = ms_ole_directory_new (ole);
132
	g_assert (dir);
Michael Meeks's avatar
Michael Meeks committed
133
	while (ms_ole_directory_next(dir)) {
Michael Meeks's avatar
Michael Meeks committed
134
		printf ("'%25s' : type %d, length %d bytes\n", dir->name, dir->type, dir->length);
Michael Meeks's avatar
Michael Meeks committed
135 136 137 138 139 140 141 142 143
	}
}

static void
syntax_error(char *err)
{
	if (err) {
		printf("Error; '%s'\n",err);
		exit(1);
Michael Meeks's avatar
Michael Meeks committed
144
	}
Michael Meeks's avatar
Michael Meeks committed
145 146 147 148 149 150
		
	printf ("Sytax:\n");
	printf (" ole <ole-file> [-i] [commands...]\n\n");
	printf (" -i: Interactive, queries for fresh commands\n\n");
	printf ("command can be one or all of:\n");
	printf (" * ls:                   list files\n");
151 152 153 154
	printf (" * biff    <stream name>:   dump biff records, merging continues\n");
	printf (" * biffraw <stream name>:   dump biff records no merge + raw data\n");
	printf (" * draw    <stream name>:   dump drawing records\n");
	printf (" * dump    <stream name>:   dump stream\n");
155 156 157
	printf (" Raw transfer commands\n");
	printf (" * get     <stream name> <fname>\n");
	printf (" * put     <fname> <stream name>\n");
Michael Meeks's avatar
Michael Meeks committed
158
	printf (" * copyin  [<fname>,]...\n");
Michael Meeks's avatar
Michael Meeks committed
159 160
	printf (" * quit,exit,bye:        exit\n");
	exit(1);
Michael Meeks's avatar
Michael Meeks committed
161 162
}

163 164
/* ---------------------------- Start cut from ms-escher.c ---------------------------- */

165

166
typedef struct { /* See: S59FDA.HTM */
Michael Meeks's avatar
Michael Meeks committed
167 168 169 170 171 172 173
	guint    ver:4;
	guint    instance:12;
	guint16  type;   /* fbt */
	guint32  length;
	guint8  *data;
	guint32  length_left;
	gboolean first;
174
} ESH_HEADER;
Michael Meeks's avatar
Michael Meeks committed
175
#define ESH_HEADER_LEN 8
176 177 178 179 180

static ESH_HEADER *
esh_header_new (guint8 *data, gint32 length)
{
	ESH_HEADER *h = g_new (ESH_HEADER,1);
Michael Meeks's avatar
Michael Meeks committed
181
	h->length=0;
182 183 184 185
	h->type=0;
	h->instance=0;
	h->data=data;
	h->length_left=length;
Michael Meeks's avatar
Michael Meeks committed
186
	h->first = TRUE;
187 188 189 190 191 192 193 194 195 196
	return h;
}

static int
esh_header_next (ESH_HEADER *h)
{
	guint16 split;
	g_return_val_if_fail(h, 0);
	g_return_val_if_fail(h->data, 0);

Michael Meeks's avatar
Michael Meeks committed
197
	if (h->length_left < h->length + ESH_HEADER_LEN*2)
198
		return 0;
Michael Meeks's avatar
Michael Meeks committed
199 200 201 202 203 204 205 206 207

	if (h->first==TRUE)
		h->first = FALSE;
	else {
		h->data+=h->length+ESH_HEADER_LEN;
		h->length_left-=h->length+ESH_HEADER_LEN;
	}

	h->length   = BIFF_GETLONG(h->data+4);
208 209 210 211
	h->type     = BIFF_GETWORD(h->data+2);
	split       = BIFF_GETWORD(h->data+0);
	h->ver      = (split&0x0f);
	h->instance = (split>>4);
212 213 214 215
#if ESH_HEADER_DEBUG > 0
	printf ("Next header length 0x%x(=%d), type 0x%x, ver 0x%x, instance 0x%x\n",
		h->length, h->length, h->type, h->ver, h->instance);
#endif
216 217
	return 1;
}
218 219 220 221 222 223 224 225 226 227 228 229

static ESH_HEADER *
esh_header_contained (ESH_HEADER *h)
{
	if (h->length_left<ESH_HEADER_LEN)
		return NULL;
	g_assert (h->data[h->length_left-1] == /* Check that pointer */
		  h->data[h->length_left-1]);
	return esh_header_new (h->data+ESH_HEADER_LEN,
			       h->length-ESH_HEADER_LEN);
}

230 231 232 233 234 235 236 237 238 239 240 241
static void
esh_header_destroy (ESH_HEADER *h)
{
	if (h)
		g_free(h);
}
/**
 *  Builds a flat record by merging CONTINUE records,
 *  Have to do until we move this into ms_ole.c
 *  pass pointers to your length & data variables.
 *  This is dead sluggish.
 **/
242
static int
243 244 245 246
biff_to_flat_data (const BIFF_QUERY *q, guint8 **data, guint32 *length)
{
	BIFF_QUERY *nq = ms_biff_query_copy (q);
	guint8 *ptr;
247
	int cnt=0;
248 249 250 251 252

	*length=0;
	do {
		*length+=nq->length;
		ms_biff_query_next(nq);
253 254 255 256
		cnt++;
	} while (nq->opcode == BIFF_CONTINUE ||
		 nq->opcode == BIFF_MS_O_DRAWING ||
		 nq->opcode == BIFF_MS_O_DRAWING_GROUP);
257

258
	printf ("MERGING %d continues\n", cnt);
259 260 261 262 263 264 265
	(*data) = g_malloc (*length);
	ptr=(*data);
	nq = ms_biff_query_copy (q);
	do {
		memcpy (ptr, nq->data, nq->length);
		ptr+=nq->length;
		ms_biff_query_next(nq);
266 267 268 269
	} while (nq->opcode == BIFF_CONTINUE ||
		 nq->opcode == BIFF_MS_O_DRAWING ||
		 nq->opcode == BIFF_MS_O_DRAWING_GROUP);
	return cnt;
270 271 272 273
}

/* ---------------------------- End cut ---------------------------- */

Michael Meeks's avatar
Michael Meeks committed
274
static void
275
dump_escher (guint8 *data, guint32 len, int level)
Michael Meeks's avatar
Michael Meeks committed
276 277 278
{
	ESH_HEADER *h = esh_header_new (data, len);
	while (esh_header_next(h)) {
279 280 281
		int lp;
		for (lp=0;lp<level;lp++) printf ("-");
		printf ("Header: type 0x%4x : '%15s', inst 0x%x ver 0x%x len 0x%x\n",
Michael Meeks's avatar
Michael Meeks committed
282 283 284
			h->type, get_escher_opcode_name (h->type), h->instance,
			h->ver, h->length);
		if (h->ver == 0xf) /* A container */
285
			dump_escher (h->data+ESH_HEADER_LEN, h->length-ESH_HEADER_LEN, level+1);
Michael Meeks's avatar
Michael Meeks committed
286 287 288 289
	}
	esh_header_destroy (h); 
}

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
static void
do_dump (MS_OLE *ole)
{
	char *ptr;
	MS_OLE_DIRECTORY *dir;

	ptr = strtok (NULL, delim);
	if ((dir = get_file_handle (ole, ptr)))
	{
		MS_OLE_STREAM *stream = ms_ole_stream_open (dir, 'r');
		guint8 *buffer = g_malloc (dir->length);
		stream->read_copy (stream, buffer, dir->length);
		printf ("Stream : '%s' length 0x%x\n", ptr, dir->length);
		if (buffer)
			dump (buffer, dir->length);
		else
			printf ("Failed read\n");
		ms_ole_stream_close (stream);
	} else
		printf ("Need a stream name\n");
}

static void
do_biff (MS_OLE *ole)
{
	char *ptr;
	MS_OLE_DIRECTORY *dir;
	
	ptr = strtok (NULL, delim);
	if ((dir = get_file_handle (ole, ptr)))
	{
		MS_OLE_STREAM *stream = ms_ole_stream_open (dir, 'r');
		BIFF_QUERY *q = ms_biff_query_new (stream);
		guint16 last_opcode=0xffff;
		guint32 last_length=0;
		guint32 count=0;
		while (ms_biff_query_next(q)) {
			if (q->opcode == last_opcode &&
			    q->length == last_length)
				count++;
			else {
				if (count>0)
					printf (" x %d\n", count+1);
				else
					printf ("\n");
				count=0;
				printf ("Opcode 0x%3x : %15s, length %d",
					q->opcode, get_biff_opcode_name (q->opcode), q->length);
			}
			last_opcode=q->opcode;
			last_length=q->length;
		}
		printf ("\n");
		ms_ole_stream_close (stream);
	} else
		printf ("Need a stream name\n");
}

static void
do_biff_raw (MS_OLE *ole)
{
	char *ptr;
	MS_OLE_DIRECTORY *dir;
	
	ptr = strtok (NULL, delim);
	if ((dir = get_file_handle (ole, ptr)))
	{
		MS_OLE_STREAM *stream = ms_ole_stream_open (dir, 'r');
		BIFF_QUERY *q = ms_biff_query_new (stream);
		guint8 data[4], *buffer;
		
		buffer = g_new (guint8, 65550);
		while (stream->read_copy (stream, data, 4)) {
			guint32 len=BIFF_GETWORD(data+2);
			printf ("Opcode 0x%3x : %15s, length 0x%x (=%d)\n",
				BIFF_GETWORD(data), get_biff_opcode_name (BIFF_GETWORD(data)),
				len, len);
			stream->read_copy (stream, buffer, len);
			dump (buffer, len);
			buffer[0]=0;
			buffer[len-1]=0;
		}
		ms_ole_stream_close (stream);
	} else
		printf ("Need a stream name\n");
}

static void
do_draw (MS_OLE *ole)
{
	char *ptr;
	MS_OLE_DIRECTORY *dir;

	ptr = strtok (NULL, delim);
	if ((dir = get_file_handle (ole, ptr)))
	{
		MS_OLE_STREAM *stream = ms_ole_stream_open (dir, 'r');
		BIFF_QUERY *q = ms_biff_query_new (stream);
		while (ms_biff_query_next(q)) {
			if (q->ls_op == BIFF_MS_O_DRAWING ||
			    q->ls_op == BIFF_MS_O_DRAWING_GROUP ||
			    q->ls_op == BIFF_MS_O_DRAWING_SELECTION) {
				guint8 *data;
				guint32 len;
				guint32 str_pos=q->streamPos;
				guint skip = biff_to_flat_data (q, &data, &len) - 1;
				printf("Drawing: '%s'\n", get_biff_opcode_name(q->opcode));
				dump_escher (data, len, 0);
				while (skip > 0 && ms_biff_query_next(q)) skip--;
			}
		}
		printf ("\n");
		ms_ole_stream_close (stream);
	} else
		printf ("Need a stream name\n");
}

static void
do_get (MS_OLE *ole)
{
	char *from, *to;
	MS_OLE_DIRECTORY *dir;

	from = strtok (NULL, delim);
	to   = strtok (NULL, delim);
	if ((dir = get_file_handle (ole, from)))
	{
		MS_OLE_STREAM *stream = ms_ole_stream_open (dir, 'r');
		guint8 *buffer = g_malloc (dir->length);
		FILE *f = fopen (to, "w");
		stream->read_copy (stream, buffer, dir->length);
		printf ("Stream : '%s' length 0x%x\n", from, dir->length);
		if (f && buffer) {
Michael Meeks's avatar
Michael Meeks committed
423
			fwrite (buffer, 1, dir->length, f);
424 425 426 427 428 429 430 431 432 433
			fclose (f);
		} else
			printf ("Failed write to '%s'\n", to);
		ms_ole_stream_close (stream);

	} else
		printf ("Need a stream name\n");
}

static void
Michael Meeks's avatar
Michael Meeks committed
434
really_put (MS_OLE *ole, char *from, char *to)
435 436 437
{
	MS_OLE_DIRECTORY *dir;
	MS_OLE_STREAM *stream;
Michael Meeks's avatar
Michael Meeks committed
438
	char buffer[8200];
439 440

	if (!from || !to) {
Michael Meeks's avatar
Michael Meeks committed
441
		printf ("Null name\n");
442 443
		return;
	}
Michael Meeks's avatar
Michael Meeks committed
444

445
	if (!(dir = get_file_handle (ole, to)))
446
		dir = ms_ole_directory_create (ms_ole_get_root(ole),
447 448 449 450 451 452
					       to, MS_OLE_PPS_STREAM);
		
	if (dir)
	{
		FILE *f = fopen (from, "r");
		size_t len;
453
		int block=0;
454 455 456 457 458 459 460 461 462 463

		stream = ms_ole_stream_open (dir, 'w');
		if (!f || !stream) {
			printf ("Failed write\n");
			return;
		}

		stream->lseek (stream, 0, MS_OLE_SEEK_SET);
	       
		do {
Michael Meeks's avatar
Michael Meeks committed
464 465
			guint32 lenr = 1+ (int)(8192.0*rand()/(RAND_MAX+1.0));
			len = fread (buffer, 1, lenr, f);
466
			printf ("Transfering block %d = %d bytes\n", block++, len); 
467
			stream->write (stream, buffer, len);
468
		} while (!feof(f) && len>0);
469 470 471 472 473 474 475

		fclose (f);
		ms_ole_stream_close (stream);
	} else
		printf ("Need a stream name\n");
}

Michael Meeks's avatar
Michael Meeks committed
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
static void
do_put (MS_OLE *ole)
{
	char *from, *to;

	from = strtok (NULL, delim);
	to   = strtok (NULL, delim);

	if (!from || !to) {
		printf ("put <filename> <stream>\n");
		return;
	}

	really_put (ole, from, to);
}

static void
do_copyin (MS_OLE *ole)
{
	char *from;

	do {
		from = strtok (NULL, delim);
		if (from)
			really_put (ole, from, from);
	} while (from);
}

Michael Meeks's avatar
Michael Meeks committed
504 505
int main (int argc, char **argv)
{
Michael Meeks's avatar
Michael Meeks committed
506 507 508 509
	MS_OLE *ole;
	int lp,exit=0,interact=0;
	char *buffer = g_new (char, 1024) ;

Michael Meeks's avatar
Michael Meeks committed
510
	if (argc<2)
Michael Meeks's avatar
Michael Meeks committed
511 512 513
		syntax_error(0);

	printf ("Ole file '%s'\n", argv[1]);
514 515 516 517 518
	ole = ms_ole_open (argv[1]);
	if (!ole) {
		printf ("Creating new file '%s'\n", argv[1]);
		ole = ms_ole_create (argv[1]);
	}
Michael Meeks's avatar
Michael Meeks committed
519
	if (!ole)
520
		syntax_error ("Can't open file or create new one");
Michael Meeks's avatar
Michael Meeks committed
521

522 523 524
	if (argc<=2)
		syntax_error ("Need command or -i");

Michael Meeks's avatar
Michael Meeks committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
	if (argc>2 && argv[argc-1][0]=='-'
	    && argv[argc-1][1]=='i') 
		interact=1;
	else {
		char *str=g_strdup(argv[2]) ;
		for (lp=3;lp<argc;lp++)
			str=g_strconcat(str," ",argv[lp],NULL); /* Mega leak :-) */
		buffer = str; /* and again */
	}

	do
	{
		char *ptr;

		if (interact) {
			fprintf (stdout,"> ");
			fflush (stdout);
			fgets (buffer, 1023, stdin);
		}

		ptr = strtok (buffer, delim);
546
		if (!ptr && interact) continue;
Michael Meeks's avatar
Michael Meeks committed
547 548
		if (!interact)
			printf ("Command : '%s'\n", ptr);
Michael Meeks's avatar
Michael Meeks committed
549 550
		if (g_strcasecmp(ptr, "ls")==0) {
			list_files (ole);
551 552 553 554 555 556 557 558 559 560 561 562
		} else if (g_strcasecmp(ptr, "dump")==0)
			do_dump (ole);
		else if (g_strcasecmp(ptr, "biff")==0)
			do_biff (ole);
		else if (g_strcasecmp(ptr, "biffraw")==0)
			do_biff_raw (ole);
		else if (g_strcasecmp(ptr, "draw")==0) /* Assume its in a BIFF file */
			do_draw (ole);
		else if (g_strcasecmp(ptr, "get")==0)
			do_get (ole);
		else if (g_strcasecmp(ptr, "put")==0)
			do_put (ole);
Michael Meeks's avatar
Michael Meeks committed
563 564
		else if (g_strcasecmp(ptr, "copyin")==0)
			do_copyin (ole);
565
		else if (g_strcasecmp(ptr,"exit")==0 ||
Michael Meeks's avatar
Michael Meeks committed
566 567 568
			   g_strcasecmp(ptr,"quit")==0 ||
			   g_strcasecmp(ptr,"bye")==0)
			exit = 1;
Michael Meeks's avatar
Michael Meeks committed
569
	}
Michael Meeks's avatar
Michael Meeks committed
570 571 572 573
	while (!exit && interact);

	ms_ole_destroy (ole);
	return 1;
Michael Meeks's avatar
Michael Meeks committed
574
}