diff --git a/exec/binarydecoder.js b/exec/binarydecoder.js index 260bcb8bff5ad2a2190b88c05d2970b82b09df88..b19509bced1e4b8acdf6a98b099d2fa3d2e43250 100644 --- a/exec/binarydecoder.js +++ b/exec/binarydecoder.js @@ -12,6 +12,9 @@ const REVISION = "$Revision$".split(' ')[1]; printf("Synchronet Binary Decoder %s session started\r\n", REVISION); +lines_per_yield=5; +completed_files=0; + var ini_fname = system.ctrl_dir + "binarydecoder.ini"; file = new File(ini_fname); @@ -56,6 +59,8 @@ for(i in sub) { if(attachment_dir.substr(-1)!='/') attachment_dir+="/"; + /* save for later */ + msg_area.sub[sub[i].code].attachment_dir=attachment_dir; /* Read MD5 list */ md5_fname=attachment_dir + "md5.lst"; @@ -84,11 +89,14 @@ for(i in sub) { parts_file.close(); } - printf("Scanning %s\r\n",sub[i].code); + printf("Scanning %s\r\n",msgbase.cfg.code); - for(;ptr<=msgbase.last_msg && bbs.online;ptr++) { + last_msg=msgbase.last_msg; + for(;ptr<=last_msg && bbs.online;ptr++) { - printf("%s %ld\r\n",sub[i].code,ptr); + console.inkey(); /* allow ^O toggle */ + printf("%s %lu of %lu\r\n" + ,msgbase.cfg.code, ptr, last_msg); hdr = msgbase.get_msg_header( /* retrieve by offset? */ false, @@ -118,49 +126,70 @@ for(i in sub) { continue; } + var part=0; + + for(o in parts_list) { + obj=parts_list[o]; + if(obj.codec=="uue" + && (part=compare_subject(hdr.subject,obj.subject))!=0) { + fname=obj.name.toString(); + file.uue=true; + } + } + var begin,end; + var first_line=0; lines=body.split("\r\n"); for(li=0;li<lines.length;li++) { + if(lines_per_yield && li && (li%lines_per_yield)==0) + yield(); + line=lines[li]; -// line=truncstr(lines[li],"\r\n"); if(file.uue && line=="end") { if(!part) if(!complete_file(file,fname,attachment_dir)) console.pause(); + li--; break; } if(file.yenc && line.substr(0,6)=="=yend " && (part_size=line.indexOf(" size="))>0) { + printf("yEnc trailer: %s\r\n",line); file.flush(); if(part) { if((end_part=line.indexOf(" part="))<0) { - printf("!yEnd part number (%ld) missing in trailer: %s\r\n",part,line); + printf("!yEnd part number (%ld) missing in trailer: %s\r\n" + ,part,line); continue; } end_part=parseInt(line.slice(end_part+6),10); if(end_part!=part) { - printf("!yEnd part number mismatch, %ld in header, trailer: %s\r\n",part,line); + printf("!yEnd part number mismatch, %ld in header, trailer: %s\r\n" + ,part,line); continue; } } part_size=parseInt(line.slice(part_size+6),10); if(part_size!=end-(begin-1)) { - printf("!yEnc part size mismatch, %ld in header, trailer: %s\r\n",end-(begin-1),line); + printf("!yEnc part size mismatch, %ld in header, trailer: %s\r\n" + ,end-(begin-1),line); printf("begin=%ld end=%ld\r\n",begin,end); continue; } if(part_size!=file.length) { - printf("!yEnc part size mismatch, actual: %ld, trailer: %s\r\n",file.length,line); + printf("!yEnc part size mismatch, actual: %ld, trailer: %s\r\n" + ,file.length,line); continue; } if(!part && size!=part_size) { - printf("!yEnc single part size mismatch: %ld in header, trailer: %s\r\n",size,line); + printf("!yEnc single part size mismatch: %ld in header, trailer: %s\r\n" + ,size,line); continue; } @@ -173,7 +202,8 @@ for(i in sub) { file_crc32=file.crc32; if(crc32!=undefined && crc32!=file_crc32) { - printf("!yEnc part CRC-32 mismatch, actual: %08lX, trailer: %s\r\n",file_crc32,line); + printf("!yEnc part CRC-32 mismatch, actual: %08lX, trailer: %s\r\n" + ,file_crc32,line); continue; } part_crc32=crc32; @@ -200,13 +230,15 @@ for(i in sub) { file.length=0; /* truncate temp file */ file.yenc=false; /* add to parts database */ - if(!add_part(parts_list,sub[i].code,hdr,fname,part,total,begin,end,part_crc32,size,crc32)) + if(!add_part(parts_list,msgbase.cfg.code,hdr + ,fname,"yenc" /* codec */ + ,part,total,first_line,li-1 /* last_line */ + ,begin,end,part_crc32,size,crc32)) console.pause(); continue; } - if(!complete_file(file,fname,attachment_dir)) - console.pause(); + complete_file(file,fname,attachment_dir); break; } @@ -223,6 +255,7 @@ for(i in sub) { arg.splice(0,2); // strip "begin 666 " fname=file_getname(arg.join(" ")); file.uue=true; + first_line=li+1; continue; } @@ -263,25 +296,30 @@ for(i in sub) { end=size; } file.yenc=true; + first_line=li+1; continue; } } /* for(li in lines) */ - if(file.is_open) { - file.close(); - if(file.uue) { - /* adds to parts database */ - } - file.remove(); + if(file.is_open && file.uue) { + /* adds to parts database */ + if(!add_part(parts_list,msgbase.cfg.code,hdr + ,fname,"uue" /* codec */ + ,part,undefined + ,first_line,li-1 /* last_line */ + )) + console.pause(); } + file.remove(); delete file; yield(); } /* for(ptr<=msg.last_msg) */ /* Save the scan pointer */ + print("Saving scan pointer"); if(ptr_file.open("w")) ptr_file.writeln(ptr); @@ -289,6 +327,7 @@ for(i in sub) { delete msgbase; /* Save Attachment MD5 history */ + print("Saving MD5 history"); if(md5_list.length) { if(md5_file.open("w")) { md5_file.writeAll(md5_list); @@ -297,6 +336,7 @@ for(i in sub) { } /* Save Attachment CRC-32 history */ + print("Saving CRC-32 History"); if(crc_list.length) { if(crc_file.open("w")) { crc_file.writeAll(crc_list); @@ -305,15 +345,20 @@ for(i in sub) { } /* Combine and decode parts */ + print("Combining partial files"); combine_parts(parts_list); /* Save the Partial file/parts Database */ + print("Saving partial file database"); parts_file.open("w"); for(o in parts_list) { obj=parts_list[o]; + if(obj==undefined) + continue; parts_file.writeln("[" + obj.name + "]"); for(prop in obj) - parts_file.printf("%-15s=%s\r\n",prop,obj[prop]); + parts_file.printf("%-20s=%s\r\n" + ,prop.toString(), obj[prop].toString()); parts_file.writeln("; end of file: " + obj.name ); parts_file.writeln(); } @@ -322,12 +367,59 @@ for(i in sub) { } /* for(i in subs) */ +printf("Synchronet Binary Decoder %s session complete (%lu files completed)\r\n" + ,REVISION, completed_files); + exit(); /* all done */ +/****************************************************************************/ +/* Compares two message subjects (subj1==new msg, subj2=file in database */ +/* returning 0 if they differ in anything but decimal digits (or identical) */ +/* returning the parsed part number otherwise */ +/****************************************************************************/ +function compare_subject(subj1, subj2) +{ + /* first compare lengths */ + length=subj1.length; + if(!length) + return(0); + if(length!=subj2.length) /* must be same length */ + return(0); + if(subj1==subj2) /* but not duplicate part */ + return(0); + + diff=""; + for(i=0;i<length;i++) { + if(subj1.charAt(i)!=subj2.charAt(i)) + diff+=(subj1.charAt(i)+subj2.charAt(i)); + } + ascii_0=ascii('0'); + ascii_9=ascii('9'); + length=diff.length; + for(i=0;i<length;i++) { + ch=ascii(diff.charAt(i)); + if(ch<ascii_0 || ch>ascii_9) + break; + } + if(i<length) /* differ in non-digit char */ + return(0); + + /* parse part number */ + part=subj1.lastIndexOf('('); + if(part>=0) + part=parseInt(subj1.slice(part+1),10); + if(part>0) + return(part); + return(0); +} + /***********************************************************************************/ /* Adds a part of a binary file to the parts database for later combine and decode */ /***********************************************************************************/ -function add_part(list,sub_code,hdr,fname,part,total,begin,end,pcrc32,size,crc32) +function add_part(list,sub_code,hdr + ,fname,codec,part,total + ,first_line,last_line + ,begin,end,pcrc32,size,crc32) { if(part<1) { /* must parse from subject (yuck) */ part=hdr.subject.lastIndexOf('('); @@ -339,7 +431,7 @@ function add_part(list,sub_code,hdr,fname,part,total,begin,end,pcrc32,size,crc32 } printf("Parsed part number: %u\r\n",part); } - if(total<part) { /* must parse from subject (yuck) */ + if(total==undefined || total<part) { /* must parse from subject (yuck) */ total=hdr.subject.lastIndexOf('('); if(total>=0) { subject=hdr.subject.slice(total+1); @@ -357,46 +449,66 @@ function add_part(list,sub_code,hdr,fname,part,total,begin,end,pcrc32,size,crc32 /* Search database for existing file object */ var li; for(li=0; li<list.length; li++) - if(list[li].name==fname && list[li].total==total + if(list[li].name==fname && list[li].total==total && list[li].codec==codec && (size==undefined || list[li].size==size) && (crc32==undefined || parseInt(list[li].crc32,16)==crc32) ) { - printf("%s found in database\r\n",fname); + printf("%s found in database\r\n",fname.toString()); break; } obj=list[li]; + var time_str = system.timestr(); + var time_hex = format("%lx",time()); + if(obj==undefined) { /* new entry */ - printf("New parts database entry for: %s\r\n",fname); + printf("New parts database entry for: %s\r\n",fname.toString()); printf("total=%u, size=%u, crc32=%lx\r\n",total,size,crc32); - obj = { name: fname, total: total, parts: 0}; + obj = { name: fname, codec: codec, parts: 0, total: total }; + + obj.subject=hdr.subject; /* save first subject for additional UUE parts */ + obj.from=hdr.from; /* yEnc file fields */ if(size!=undefined) obj.size=size; if(crc32!=undefined) obj.crc32=format("%lx",crc32); + + /* Timestamp */ + obj.created=time_str; + obj.created_time=time_hex } + /* Timestamp */ + obj.updated=time_str; + obj.updated_time=time_hex; + /* part message info */ prop=format("part%u.id",part); if(obj[prop]!=undefined) { - printf("Part %u of %s already in database\r\n",part,fname); - return(false); + printf("Part %u of %s already in database\r\n",part,fname.toString()); + return(true); // pretend we added it } - printf("Adding part %u/%u of %s to database\r\n",part,total,fname); + printf("Adding part %u of %s-encoded file to database:\r\n",part,codec.toUpperCase()); + printf("File: %s\r\n",fname); obj[format("part%u.id",part)]=hdr.id; obj[format("part%u.sub",part)]=sub_code; obj[format("part%u.msg",part)]=hdr.number; - obj[format("part%u.from",part)]=hdr.from; - obj[format("part%u.subj",part)]=hdr.subject; obj[format("part%u.date",part)]=hdr.date; - obj[format("part%u.added",part)]=system.timestr(); + obj[format("part%u.added",part)]=time_str; + obj[format("part%u.added_time",part)]=time_hex; +// obj[format("part%u.from",part)]=hdr.from; +// obj[format("part%u.subject",part)]=hdr.subject; + + /* These save us the hassle of parsing again later */ + obj[format("part%u.first_line",part)]=first_line; + obj[format("part%u.last_line",part)]=last_line; /* yEnc part fields */ if(begin!=undefined) @@ -405,12 +517,13 @@ function add_part(list,sub_code,hdr,fname,part,total,begin,end,pcrc32,size,crc32 obj[format("part%u.end",part)]=end; if(pcrc32!=undefined) obj[format("part%u.crc32",part)]=format("%lx",pcrc32); - + obj.parts++; list[li]=obj; - printf("Part %u/%u added (%u parts in database)\r\n",part,total,obj.parts); + printf("Part %u added (%u/%u parts in database)\r\n" + ,part,obj.parts,obj.total); return(true); } @@ -418,13 +531,78 @@ function add_part(list,sub_code,hdr,fname,part,total,begin,end,pcrc32,size,crc32 /****************************************************************************/ /* Combine parts for complete files */ /****************************************************************************/ -function combine_parts(list) +function combine_parts(list,attachment_dir) { + var li; for(li=0; li<list.length; li++) { if(list[li].parts!=list[li].total) continue; - printf("File complete: %s (%s parts)\r\n" - ,list[li].name,list[li].parts.toString()); + var obj=list[li]; + var sub_code; + printf("File complete: %s (%u parts)\r\n", obj.name, obj.parts); + + file = new File(system.temp_dir + "binary.tmp"); + if(!file.open("w+b")) { + printf("!ERROR %d opening/creating %s\r\n", file.error, file.name); + continue; + } + file[obj.codec]=true; /* yenc or uue */ + + var pi; + for(pi=1;pi<=obj.total;pi++) { + printf("Processing part %u of %u\r\n",pi,obj.total); + var prefix=format("part%u.",pi); + sub_code=obj[prefix + "sub"]; + msgbase=new MsgBase(sub_code); + if(msgbase.open()==false) { + printf("!ERROR %s opening msgbase: %s\r\n",msgbase.last_error,sub_code); + delete msgbase; + break; + } + ptr=obj[prefix + "msg"]; + body = msgbase.get_msg_body( + false /* retrieve by offset */ + ,ptr /* message number */ + ,false /* remove ctrl-a codes */ + ,false /* rfc822 formatted text */ + ,false /* include tails */ + ); + if(body == null) { + printf("!FAILED to read message number %ld\r\n",ptr); + break; + } + + lines=body.split("\r\n"); + + first_line=obj[prefix + "first_line"]; + last_line=obj[prefix + "last_line"]; + for(var l=first_line;l<=last_line;l++) + file.write(lines[l]); + } + if(pi<=obj.total) { + file.remove(); + printf("!Combine failure, removing part %u\r\n",pi); + list[li][format("part%u.id",pi)]=undefined; + continue; + } + + /* Verify final CRC-32, if available */ + if(obj.crc32!=undefined) { + file_crc32=format("%lx",file.crc32); + if(obj.crc32!=file_crc32) { + file.remove(); + printf(!"CRC-32 failure, actual: %s, expected: %s\r\n" + ,file_crc32, obj.crc32); + continue; + } + } + + complete_file(file + ,obj.name.toString() + ,msg_area.sub[sub_code].attachment_dir); + + list[li]=undefined; // Remove file entry from database + } } @@ -477,6 +655,6 @@ function complete_file(file, fname, attachment_dir) } printf("Attachment saved as: %s\r\n",fname); - + completed_files++; return(true); } \ No newline at end of file