aboutsummaryrefslogtreecommitdiffstats
path: root/transfug_oe.c
blob: 04a60704948ac23e5c6ab882587fc9790eb12f9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/* 
 * transfugdrake
 * (c) 2001 Yves Duret <yduret@mandrakesof.com>
 * $Id: transfug_oe.c 225151 2006-12-09 03:13:01Z pablo $
 */

/*  This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* based on LIBOE 0.92 - STABLE
   Copyright (C) 2000 Stephan B. NedregÄrd (stephan@micropop.com) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#define OE_CANNOTREAD 1
#define OE_NOTOEBOX   2
#define OE_POSITION   3
#define OE_NOBODY     4
#define OE_PANIC      5

#define SLASH '/'


/* #define DEBUG -- uncomment to get some DEBUG output to stdout */


/* TABLE STRUCTURES 
   -- tables store pointers to message headers and other tables also
   containing pointers to message headers and other tables -- */

struct oe_table_header { /* At the beginning of each table */
  int self,   /* Pointer to self (filepos) */
    unknown1, /* Unknown */
    list,     /* Pointer to list */
    next,     /* Pointer to next */
    unknown3, /* Unknown */
    unknown4; /* Unknown */
};
typedef struct oe_table_header oe_table_header;

struct oe_table_node { /* Actual table entries */
  int message, /* Pointer to message | 0 */
    list,   /* Pointer to another table | 0 */
    unknown; /* Unknown */
};
typedef struct oe_table_node oe_table_node;

struct oe_list { /* Internal use only */
  long pos;
  struct oe_list *next;
};
typedef struct oe_list oe_list;



/* MESSAGE STRUCTURES
   -- OE uses 16-byte segment headers inside the actual messages. These 
   are meaningful, as described below, but were not very easy to hack
   correctly -- note that a message may be composed of segments located
   anywhere in the mailbox file, some times far from each other. */

struct oe_msg_segmentheader {
  int self,  /* Pointer to self (filepos) */
    increase, /* Increase to next segment header (not in msg, in file!) */
    include, /* Number of bytes to include from this segment */
    next,  /* Pointer to next message segment (in msg) (filepos) */
    usenet; /* Only used with usenet posts */
};
typedef struct oe_msg_segmentheader oe_msg_segmentheader;




/* INTERAL STRUCTURES */
struct oe_internaldata{
  void (*oput)(char*);
  FILE *oe;
  oe_list *used;
  int success, justheaders, failure;
  int errcode;
  struct stat *stat;
};
typedef struct oe_internaldata oe_data;



/* LIST OF USED TABLES */

void oe_posused(oe_data *data, long pos) {
  oe_list *n = malloc(sizeof(oe_list));
  n->pos = pos;
  n->next = data->used;
  data->used = n;
}

int oe_isposused(oe_data *data, long pos) {
  oe_list *n = data->used;
  while (n!=NULL) {
    if (pos==n->pos) return 1;
    n = n->next;
  }
  return 0;
}

void oe_freeposused(oe_data *data) {
  oe_list *n;
  while (data->used!=NULL) {n=data->used->next; free(data->used); data->used=n;}
}


/* ACTUAL MESSAGE PARSER */

int oe_readmessage(oe_data *data, 
		   long pos, 
		   int newsarticle) {
  int segheadsize = sizeof(oe_msg_segmentheader)-4; /*+(newsarticle<<2);*/
  oe_msg_segmentheader *sgm = malloc(sizeof(oe_msg_segmentheader));
  char buff[16], *ss = malloc(2048), *s = ss;
  int nextsegment, endofsegment, i, headerwritten = 0;
  fseek(data->oe,pos, SEEK_SET);
  while (1) {
    fread(sgm,segheadsize,1,data->oe);
    if (pos!=sgm->self) { /* No body found*/
#ifdef DEBUG
      printf("- Fail reported at %.8x (%.8x)\n",pos,sgm->self); 
#endif
      free(sgm); 
      free(ss); 
      data->failure++; 
      return OE_NOBODY;
    } 
    pos+=segheadsize;
    nextsegment = pos+sgm->increase;
    endofsegment = pos+sgm->include;
    if (!headerwritten) {
#ifdef DEBUG
      printf("%.8x : \n",pos-segheadsize);
#endif
      data->oput("From mandrake@MandrakeLinux Mon Jun 11 10:00:00 2001\n");
      headerwritten = 1;
    }
    while (pos<endofsegment) {
      fread(&buff,1,16,data->oe);
      for (i=0;i<16;i++,pos++) 
	if ((pos<endofsegment) && (buff[i]!=0x0d)) { /* rm extra DOS newline */
	  *(s++)=buff[i]; 
	  if (buff[i]==0x0a) { *s='\0'; data->oput(ss); s=ss; }
	}
    }
    fseek(data->oe,sgm->next, SEEK_SET);
    pos = sgm->next;
    if (pos==0) break;
  }
  if (s!=ss) {
    strcpy(s,"\n");
    data->oput(s);
  }
  data->oput("\n");

  data->success++;
  free(sgm);
  free(ss);
  return 0;
}


/* PARSES MESSAGE HEADERS */

int oe_readmessageheader(oe_data *data, long pos) {
  int segheadsize = sizeof(oe_msg_segmentheader)-4;
  oe_msg_segmentheader *sgm;
  int self=1, msgpos = 0, newsarticle = 0;

  if (oe_isposused(data,pos)) return 0; else oe_posused(data,pos);
  fseek(data->oe,pos, SEEK_SET);
  sgm = malloc(sizeof(oe_msg_segmentheader));
  fread(sgm,segheadsize,1,data->oe);
  if (pos!=sgm->self) { free(sgm); return OE_POSITION; /* ERROR */ }
  free(sgm);

  fread(&self,4,1,data->oe); self=1;
  while ((self & 0x7F)>0) {
    fread(&self,4,1,data->oe);
    if ((self & 0xFF) == 0x84) /* 0x80 = Set, 04 = Index */
      if (msgpos==0)
      msgpos = self >> 8; 
    if ((self & 0xFF) == 0x83) /* 0x80 = Set, 03 = News  */
      newsarticle = 1;
  }
  if (msgpos) oe_readmessage(data,msgpos,newsarticle); else  { 
    fread(&self,4,1,data->oe);
    fread(&msgpos,4,1,data->oe);
    if (oe_readmessage(data,msgpos,newsarticle)) { 
      if (newsarticle) {
	data->justheaders++; 
	data->failure--; 
      }
    }
  }
  return 0;
}


/* PARSES MAILBOX TABLES */

int oe_readtable(oe_data *data, long pos) {
  oe_table_header thead;
  oe_table_node tnode;
  int quit = 0;

  if (oe_isposused(data,pos)) return 0;

  fseek(data->oe,pos, SEEK_SET);

  fread(&thead,sizeof(oe_table_header),1,data->oe);
  if (thead.self != pos) return OE_POSITION;
  oe_posused(data,pos);
  pos+=sizeof(oe_table_header);

  oe_readtable(data,thead.next);
  oe_readtable(data,thead.list);
  fseek(data->oe,pos, SEEK_SET); 

  while (!quit) {
    fread(&tnode,sizeof(oe_table_node),1,data->oe);
    pos+=sizeof(oe_table_node);
    if ( (tnode.message > data->stat->st_size) && 
	 (tnode.list > data->stat->st_size) ) 
      return 0xF0; /* PANIC */
    if ( (tnode.message == tnode.list) && /* Neither message nor list==quit */
	 (tnode.message == 0) ) quit = 1; else {
	   oe_readmessageheader(data,tnode.message);
	   oe_readtable(data,tnode.list);
	 }
    fseek(data->oe,pos, SEEK_SET);
  }

  return 0;
}

void oe_readdamaged(oe_data *data) { 
  /* If nothing else works (needed this to get some mailboxes 
     that even OE couldn't read to work. Should generally not 
     be needed, but is nice to have in here */
  long pos = 0x7C;
  int i,check, lastID;
#ifdef DEBUG
  printf("  Trying to construct internal mailbox structure\n");
#endif
  fseek(data->oe,pos, SEEK_SET);
  fread(&pos,sizeof(int),1,data->oe); 
  if (pos==0) return; /* No, sorry, didn't work */
  fseek(data->oe,pos, SEEK_SET);
  fread(&i,sizeof(int),1,data->oe);
  if (i!=pos) return; /* Sorry */
  fread(&pos,sizeof(int),1,data->oe);
  i+=pos+8;
  pos = i+4;
  fseek(data->oe,pos, SEEK_SET);
#ifdef DEBUG
  printf("  Searching for %.8x\n",i);
#endif
  lastID=0;
  while (pos<data->stat->st_size) {
    /* Read through file, notice markers, look for message (gen. 2BD4)*/
    fread(&check,sizeof(int),1,data->oe); 
    if (check==pos) lastID=pos;
    pos+=4;
    if ((check==i) && (lastID)) {
#ifdef DEBUG
      printf("Trying possible table at %.8x\n",lastID);
#endif
      oe_readtable(data,lastID);
      fseek(data->oe,pos, SEEK_SET);
    }
  }
}

void oe_readbox_oe4(oe_data *data) {
  long pos = 0x54, endpos=0, i;
  oe_msg_segmentheader *header=malloc(sizeof(oe_msg_segmentheader));
  char *cb = malloc(4), *sfull = malloc(65536), *s = sfull;
  fseek(data->oe,pos, SEEK_SET); 
  while (pos<data->stat->st_size) {
    fseek(data->oe,pos, SEEK_SET);
    fread(header,16,1,data->oe);
    data->oput("From mandrake@MandrakeLinux Mon Jun 11 10:00:00 2001\n");
    endpos = pos + header->include;
    if (endpos>data->stat->st_size) endpos=data->stat->st_size;
    pos+=4;
    while (pos<endpos) {
      fread(cb,1,4,data->oe);
      for (i=0;i<4;i++,pos++) 
	if (*(cb+i)!=0x0d) {
	  *s++ = *(cb+i);
	  if (*(cb+i) == 0x0a) {
	    *s = '\0';
	    data->oput(sfull);
	    s = sfull;
	  }
	}
    }
    data->success++;
    if (s!=sfull) { *s='\0'; data->oput(sfull); s=sfull; }
    data->oput("\n");
    pos=endpos;
  }
  free(header);
  free(sfull);
  free(cb);
}

/* CALL THIS ONE */

oe_data* oe_readbox(char* filename,void (*oput)(char*)) {
  int signature[4], i;
  oe_data *data = malloc(sizeof(oe_data));
  data->success=data->failure=data->justheaders=data->errcode=0;
  data->used = NULL;
  data->oput = oput;
  data->oe = fopen(filename,"rb");
  if (data->oe==NULL) {
    fclose(data->oe);
    data->errcode = OE_CANNOTREAD;
    return data;
  }

  /* SECURITY (Yes, we need this, just in case) */
  data->stat = malloc(sizeof(struct stat));
  stat(filename,data->stat); 
  
  /* SIGNATURE */
  fread(&signature,16,1,data->oe); 
  if ((signature[0]!=0xFE12ADCF) || /* OE 5 & OE 5 BETA SIGNATURE */
      (signature[1]!=0x6F74FDC5) ||
      (signature[2]!=0x11D1E366) ||
      (signature[3]!=0xC0004E9A)) {
    if ((signature[0]==0x36464D4A) &&
	(signature[1]==0x00010003)) /* OE4 SIGNATURE */ {
      oe_readbox_oe4(data);
      fclose(data->oe);
      free(data->stat);
      return data;
    }
    fclose(data->oe);
    free(data->stat);
    data->errcode = OE_NOTOEBOX;
    return data;
  }

  /* ACTUAL WORK */
  i = 0x30;
  fseek(data->oe,i, SEEK_SET);
  fread(&i,4,1,data->oe);
  if (!i) i=0x1e254;
  i = oe_readtable(data,i); /* Reads the box */
  if (i & 0xF0) {
    oe_readdamaged(data);
    data->errcode=OE_PANIC;
  }
  oe_freeposused(data);

  /* CLOSE DOWN */
  fclose(data->oe);
  free(data->stat);
  return data;
}

#define buffsize 65536

/* Just a function to provide the same kind of stream 
   for ordinary mailboxes. */
oe_data* oe_readmbox(char* filename,void (*oput)(char*)) {
  oe_data *data = malloc(sizeof(oe_data));
  char *s = malloc(buffsize);
  data->success=data->failure=data->justheaders=0;
  data->used=NULL;
  data->oe=fopen(filename,"rb");
  for (;;) {
    s=fgets(s,buffsize,data->oe);
    if (s==NULL) break; else oput(s);
  }
  fclose(data->oe);
  return data;
}

/****************************************/

FILE *mbox = NULL;
char *filename = NULL, *fn;

void msgandquit(int h) {
  if (h==0)
    printf("transfug_oe\nSyntax: transfug_oe [oe_mbox]*\n"
	   "based on OE2MBX 1.21 (c) 2000 Stephan B. Nedregaard - stephan@micropop.com\n");
  else if (h==1) 
    printf("OE2MBX cannot run on this platform. Please consult the Web site at http://www.micropop.com/code/\n");
  exit(h);
}

void fatal(char *s) {
  printf("Fatal error: %s\n\n",s);
  exit(1);
}

void writeit(char *s) {
  if (mbox==NULL) {
    mbox=fopen(fn,"w");
    if(mbox==NULL) fatal("Cannot create output file");
  }
  fprintf(mbox,"%s",s);
}

int main(int argc, char*argv[]) {
  int i;
  oe_data *j;

  /* Handle errors, help and syntax */
  if (argc<3) msgandquit(0);

  /* Process mailboxes */
    printf("Converting %s...\n",argv[1]);
    fn = argv[2];
    if (*fn==SLASH) fn++;
    printf("  => %s\n",fn);
    j = (oe_data*) oe_readbox(argv[1],writeit);
    if (j!=NULL) {
      if (!j->success) printf("  No messages converted"); else
	printf("  %d messages converted",j->success);
      if (j->justheaders) printf(" (%d headers w/o bodies)",j->justheaders);
      if (j->failure) printf(" (%d messages failed)",j->failure);
      printf("\n");
    } else printf("  Empty mailbox\n");
    if (mbox!=NULL) fclose(mbox);
    mbox=NULL;
  return 0;
}