Mirror of :pserver:cvs@cvs.fefe.de:/cvs libowfat https://www.fefe.de/libowfat/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

420 lines
14 KiB

12 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
  1. /* this is a uudecode that also handles yenc encoded data. The encoded
  2. * messages are read from stdin. The yenc decoder even tries to
  3. * reconstruct mutilated encodings. One of my news servers has the
  4. * problem that it gets corrupted yenc data through one of the uplinks
  5. * that replace "^." with "^..". This decoder will try reversing this
  6. * when decoding a part with broken crc. */
  7. #include <unistd.h>
  8. #include <stdint.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <errno.h>
  12. #include "textcode.h"
  13. #include "str.h"
  14. #include "buffer.h"
  15. #include "open.h"
  16. #include "stralloc.h"
  17. #include "scan.h"
  18. #include "case.h"
  19. static const uint32_t crc_table[256] = {
  20. 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
  21. 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
  22. 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
  23. 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
  24. 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
  25. 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
  26. 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
  27. 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
  28. 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
  29. 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
  30. 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
  31. 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
  32. 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
  33. 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
  34. 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
  35. 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
  36. 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
  37. 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
  38. 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
  39. 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
  40. 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
  41. 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
  42. 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
  43. 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
  44. 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
  45. 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
  46. 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
  47. 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
  48. 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
  49. 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
  50. 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
  51. 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
  52. 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
  53. 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
  54. 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
  55. 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
  56. 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
  57. 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
  58. 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
  59. 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
  60. 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
  61. 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
  62. 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
  63. 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
  64. 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
  65. 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
  66. 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
  67. 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
  68. 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
  69. 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
  70. 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
  71. 0x2d02ef8dL
  72. };
  73. uint32_t crc32(uint32_t crc, const char* buf, unsigned int len) {
  74. const unsigned char* b=(const unsigned char*)buf;
  75. crc = crc ^ 0xfffffffful;
  76. while (len) {
  77. crc = (crc >> 8) ^ crc_table[(crc & 0xff) ^ *b];
  78. ++b; --len;
  79. }
  80. return crc ^ 0xfffffffful;
  81. }
  82. int main(int argc,char* argv[]) {
  83. char buf[4096];
  84. char obuf[4096];
  85. buffer filein;
  86. buffer fileout;
  87. int fd=0;
  88. int ofd=-1;
  89. int found=0;
  90. char line[1000]; /* uuencoded lines can never be longer than 64 characters */
  91. int l;
  92. enum { BEFOREBEGIN, AFTERBEGIN, SKIPHEADER } state=BEFOREBEGIN;
  93. enum { UUDECODE, YENC, MIME } mode=UUDECODE;
  94. enum { NONE, BASE64, QP} mimeenc=NONE;
  95. char filename[1024];
  96. unsigned long fmode=0,lineno=0;
  97. unsigned long offset,endoffset,totalsize,linelen,part,reconstructed=0; /* used only for yenc */
  98. static stralloc yencpart;
  99. unsigned int crc;
  100. if (argc>1) {
  101. fd=open_read(argv[1]);
  102. if (fd<0) {
  103. buffer_puts(buffer_2,"error: could not open \"");
  104. buffer_puts(buffer_2,argv[1]);
  105. buffer_putsflush(buffer_2,"\"\n");
  106. return 1;
  107. }
  108. }
  109. buffer_init(&filein,read,fd,buf,sizeof buf);
  110. /* skip to "^begin " */
  111. for (;;) {
  112. if ((l=buffer_getline(&filein,line,(sizeof line)-1))==0 && line[l]!='\n') {
  113. hiteof:
  114. if (state!=BEFOREBEGIN) {
  115. if (mode!=MIME) {
  116. buffer_puts(buffer_1,"premature end of file in line ");
  117. buffer_putulong(buffer_1,lineno);
  118. buffer_putsflush(buffer_1,"!\n");
  119. }
  120. if (ofd>=0) {
  121. buffer_flush(&fileout);
  122. fchmod(ofd,fmode);
  123. close(ofd);
  124. }
  125. ++found;
  126. }
  127. if (!found)
  128. buffer_putsflush(buffer_2,"warning: hit end of file without finding any uuencoded data!\n");
  129. return 0;
  130. }
  131. ++lineno;
  132. if (l>0 && line[l-1]=='\r') --l; /* kill DOS line endings */
  133. line[l]=0;
  134. if (str_start(line,"begin ")) {
  135. if (state!=BEFOREBEGIN) {
  136. buffer_puts(buffer_1,"new begin without previous end in line ");
  137. buffer_putulong(buffer_1,lineno);
  138. buffer_putsflush(buffer_1,"!\n");
  139. if (ofd>=0) {
  140. buffer_flush(&fileout);
  141. fchmod(ofd,fmode);
  142. close(ofd);
  143. }
  144. ++found;
  145. }
  146. state=BEFOREBEGIN;
  147. if (line[l=6+scan_8long(line+6,&fmode)]==' ' && fmode) {
  148. int i;
  149. ++l;
  150. mode=UUDECODE;
  151. foundfilename:
  152. if (line[l]=='"') {
  153. ++l;
  154. line[str_chr(line+l,'"')]=0;
  155. }
  156. if (line[l+(i=str_rchr(line+l,'/'))]) l+=i+1;
  157. while (line[l]=='.') ++l;
  158. if (line[l]) {
  159. if (mode==YENC)
  160. ofd=open_write(line+l);
  161. else
  162. ofd=open_excl(line+l);
  163. if (ofd<0) {
  164. buffer_puts(buffer_2,"error: could not create file \"");
  165. buffer_puts(buffer_2,line+l);
  166. buffer_putsflush(buffer_2,"\" (must not exist yet)\n");
  167. } else {
  168. if (mode!=YENC || part==1) {
  169. if (mode==YENC)
  170. buffer_puts(buffer_2,"decoding yEnc file \"");
  171. else
  172. buffer_puts(buffer_2,"uudecoding file \"");
  173. buffer_puts(buffer_2,line+l);
  174. buffer_putsflush(buffer_2,"\"\n");
  175. }
  176. state=AFTERBEGIN;
  177. buffer_init(&fileout,write,ofd,obuf,sizeof obuf);
  178. continue;
  179. }
  180. }
  181. }
  182. } else if (str_equal(line,"end")) {
  183. if (ofd>=0) {
  184. buffer_flush(&fileout);
  185. fchmod(ofd,fmode);
  186. close(ofd);
  187. ofd=-1;
  188. }
  189. ++found;
  190. state=BEFOREBEGIN;
  191. continue;
  192. } else if (str_start(line,"Content-Disposition: ")) {
  193. char* c=strstr(line,"filename=");
  194. if (!c) {
  195. if ((l=buffer_getline(&filein,line,(sizeof line)-1))==0 && line[l]!='\n') goto hiteof;
  196. c=strstr(line,"filename=");
  197. }
  198. if (c) {
  199. mode=MIME;
  200. filename[0]=0;
  201. c+=9;
  202. if (*c=='"') {
  203. char* d=strchr(c+1,'"');
  204. if (d) {
  205. *d=0;
  206. strcpy(filename,c+1);
  207. }
  208. }
  209. if (!filename[0]) {
  210. strcpy(filename,c);
  211. /* TODO: truncate at space */
  212. }
  213. if (state!=BEFOREBEGIN) {
  214. if (ofd>=0) {
  215. buffer_flush(&fileout);
  216. fchmod(ofd,fmode);
  217. close(ofd);
  218. ofd=-1;
  219. }
  220. ++found;
  221. }
  222. fmode=0644;
  223. }
  224. } else if (str_start(line,"Content-Transfer-Encoding: ")) {
  225. if (case_starts(line+27,"base64"))
  226. mimeenc=BASE64;
  227. else if (case_starts(line+27,"quoted-printable"))
  228. mimeenc=QP;
  229. else if (case_starts(line+27,"7bit"))
  230. mimeenc=NONE; /* this is not an attachment */
  231. else {
  232. buffer_puts(buffer_1,"unknown encoding \"");
  233. buffer_puts(buffer_1,line+27);
  234. buffer_puts(buffer_1,"\" on line ");
  235. buffer_putulong(buffer_1,lineno);
  236. buffer_putsflush(buffer_1,".\n");
  237. }
  238. } else if (!line[0]) {
  239. /* empty line */
  240. if (ofd==-1 && filename[0]) {
  241. ofd=open_excl(filename);
  242. if (ofd<0) {
  243. buffer_puts(buffer_2,"error: could not create file \"");
  244. buffer_puts(buffer_2,filename);
  245. buffer_putsflush(buffer_2,"\" (must not exist yet)\n");
  246. } else {
  247. buffer_puts(buffer_2,"decoding MIME attachment \"");
  248. buffer_puts(buffer_2,filename);
  249. buffer_putsflush(buffer_2,"\"\n");
  250. filename[0]=0;
  251. state=AFTERBEGIN;
  252. buffer_init(&fileout,write,ofd,obuf,sizeof obuf);
  253. continue;
  254. }
  255. }
  256. if (state==AFTERBEGIN)
  257. state=SKIPHEADER;
  258. else if (state==SKIPHEADER)
  259. state=AFTERBEGIN;
  260. } else if (str_start(line,"=ybegin ")) {
  261. char* filename=strstr(line," name=");
  262. if (!filename) {
  263. invalidybegin:
  264. buffer_puts(buffer_2,"invalid =ybegin at line ");
  265. buffer_putulong(buffer_2,lineno);
  266. buffer_putsflush(buffer_2,".\n");
  267. continue;
  268. }
  269. l=filename-line+6;
  270. if (!(filename=strstr(line," part="))) {
  271. part=1;
  272. } else if (filename[6+scan_ulong(filename+6,&part)] != ' ') goto invalidybegin;
  273. if (part==1) reconstructed=0;
  274. if (!(filename=strstr(line," size="))) goto invalidybegin;
  275. if (filename[6+scan_ulong(filename+6,&totalsize)] != ' ') goto invalidybegin;
  276. if (!(filename=strstr(line," line="))) goto invalidybegin;
  277. if (filename[6+scan_ulong(filename+6,&linelen)] != ' ') goto invalidybegin;
  278. mode=YENC;
  279. stralloc_copys(&yencpart,""); crc=0;
  280. goto foundfilename;
  281. } else if (str_start(line,"=ypart ")) {
  282. char* tmp=strstr(line," begin=");
  283. char c;
  284. if (!tmp) {
  285. invalidpart:
  286. buffer_puts(buffer_2,"invalid =ypart at line ");
  287. buffer_putulong(buffer_2,lineno);
  288. buffer_putsflush(buffer_2,".\n");
  289. continue;
  290. }
  291. c=tmp[7+scan_ulong(tmp+7,&offset)];
  292. if (c!=' ' && c!=0) goto invalidpart;
  293. if (!(tmp=strstr(line," end="))) goto invalidpart;
  294. c=tmp[5+scan_ulong(tmp+5,&endoffset)];
  295. if (c!=' ' && c!=0) goto invalidpart;
  296. --offset; --endoffset;
  297. if (endoffset<offset || endoffset>totalsize) goto invalidpart;
  298. lseek(ofd,offset,SEEK_SET);
  299. continue;
  300. } else if (str_start(line,"=yend")) {
  301. /* first try to decode it normally and see if the CRC matches */
  302. unsigned long i,wantedcrc,gotcrc;
  303. gotcrc=0;
  304. stralloc out;
  305. char* tmp=strstr(line," pcrc32=");
  306. if (tmp) {
  307. if (!scan_xlong(tmp+8,&wantedcrc))
  308. goto invalidpart;
  309. wantedcrc &= 0xfffffffful;
  310. gotcrc=1;
  311. } else if (part==1) {
  312. tmp=strstr(line," crc32=");
  313. if (!tmp) goto invalidpart;
  314. if (!scan_xlong(tmp+7,&wantedcrc))
  315. goto invalidpart;
  316. wantedcrc &= 0xfffffffful;
  317. gotcrc=1;
  318. endoffset=totalsize;
  319. } else goto invalidpart;
  320. stralloc_init(&out);
  321. stralloc_0(&yencpart);
  322. stralloc_ready(&out,yencpart.len);
  323. for (i=0; i<yencpart.len; ) {
  324. unsigned long x,scanned;
  325. x=scan_yenc(yencpart.s+i,out.s+out.len,&scanned);
  326. i+=x+1; out.len+=scanned;
  327. }
  328. i=crc32(0,out.s,out.len);
  329. if (endoffset == offset+out.len-1) ++endoffset;
  330. if (out.len == endoffset-offset && i == wantedcrc) {
  331. /* ok, save block */
  332. if (buffer_put(&fileout,out.s,out.len)) {
  333. writeerror:
  334. buffer_putmflush(buffer_1,"write error: ",strerror(errno),"\n");
  335. return 1;
  336. }
  337. } else {
  338. out.len=0;
  339. for (i=0; i<yencpart.len; ) {
  340. unsigned long x,scanned;
  341. if (yencpart.s[i]=='.' && yencpart.s[i+1]=='.') ++i;
  342. x=scan_yenc(yencpart.s+i,out.s+out.len,&scanned);
  343. i+=x+1; out.len+=scanned;
  344. }
  345. i=crc32(0,out.s,out.len);
  346. if (!gotcrc) wantedcrc=i;
  347. if (out.len == endoffset-offset && i == wantedcrc) {
  348. if (buffer_put(&fileout,out.s,out.len)) goto writeerror;
  349. ++reconstructed;
  350. } else {
  351. buffer_puts(buffer_2,"warning: part ");
  352. buffer_putulong(buffer_2,part);
  353. buffer_putsflush(buffer_2," corrupt; reconstruction failed.\n");
  354. buffer_puts(buffer_2," -> ");
  355. buffer_putulong(buffer_2,offset);
  356. buffer_puts(buffer_2,"-");
  357. buffer_putulong(buffer_2,endoffset);
  358. buffer_puts(buffer_2," (want crc ");
  359. buffer_putxlong(buffer_2,wantedcrc);
  360. buffer_puts(buffer_2,", got crc ");
  361. buffer_putxlong(buffer_2,i);
  362. buffer_putsflush(buffer_2,")\n");
  363. if (buffer_put(&fileout,out.s,out.len)) goto writeerror;
  364. }
  365. }
  366. stralloc_free(&out);
  367. if (buffer_flush(&fileout) || close(ofd)) goto writeerror;
  368. ofd=-1;
  369. if (endoffset==totalsize && reconstructed) {
  370. buffer_puts(buffer_2,"warning: had to reconstruct ");
  371. buffer_putulong(buffer_2,reconstructed);
  372. buffer_putsflush(buffer_2," parts!\n");
  373. }
  374. ++found;
  375. state=BEFOREBEGIN;
  376. continue;
  377. } else {
  378. unsigned long scanned,x=0;
  379. char tmp[1000];
  380. switch (mode) {
  381. case MIME:
  382. switch (mimeenc) {
  383. case BASE64: x=scan_base64(line,tmp,&scanned); break;
  384. case QP: x=scan_quotedprintable(line,tmp,&scanned); break;
  385. default:
  386. break;
  387. }
  388. if (line[x]) x=0;
  389. break;
  390. case UUDECODE:
  391. x=scan_uuencoded(line,tmp,&scanned);
  392. break;
  393. case YENC:
  394. stralloc_cats(&yencpart,line);
  395. stralloc_cats(&yencpart,"\n");
  396. continue;
  397. }
  398. if (!x) {
  399. if (state==AFTERBEGIN) {
  400. buffer_puts(buffer_1,"parse error in line ");
  401. buffer_putulong(buffer_1,lineno);
  402. buffer_puts(buffer_1,": \"");
  403. buffer_puts(buffer_1,line);
  404. buffer_putsflush(buffer_1,"\"\n");
  405. state=SKIPHEADER;
  406. }
  407. } else {
  408. if (ofd>=0)
  409. if (buffer_put(&fileout,tmp,scanned)) goto writeerror;
  410. }
  411. }
  412. }
  413. }