Skip to content
Snippets Groups Projects
Select Git revision
  • dailybuild_linux-x64
  • dailybuild_win32
  • master default protected
  • dd_msg_reader_use_dd_msg_area_chooser_and_area_sort_update
  • sqlite
  • rip_abstraction
  • dailybuild_macos-armv8
  • dd_file_lister_filanem_in_desc_color
  • mode7
  • dd_msg_reader_are_you_there_warning_improvement
  • c23-playing
  • syncterm-1.3
  • syncterm-1.2
  • test-build
  • hide_remote_connection_with_telgate
  • 638-can-t-control-c-during-a-file-search
  • add_body_to_pager_email
  • mingw32-build
  • cryptlib-3.4.7
  • ree/mastermind
  • sbbs320d
  • syncterm-1.6
  • syncterm-1.5
  • syncterm-1.4
  • sbbs320b
  • syncterm-1.3
  • syncterm-1.2
  • syncterm-1.2rc6
  • syncterm-1.2rc5
  • push
  • syncterm-1.2rc4
  • syncterm-1.2rc2
  • syncterm-1.2rc1
  • sbbs319b
  • sbbs318b
  • goodbuild_linux-x64_Sep-01-2020
  • goodbuild_win32_Sep-01-2020
  • goodbuild_linux-x64_Aug-31-2020
  • goodbuild_win32_Aug-31-2020
  • goodbuild_win32_Aug-30-2020
40 results

newslink.js

Blame
  • To find the state of this project's repository at the time of any of these versions, check out the tags.
    newslink.js 25.37 KiB
    // newslink.js
    
    // Synchronet Newsgroup Link/Gateway Module
    
    // Configuration file (in ctrl/newslink.cfg) format:
    
    // ;this line is a comment
    // server	servername
    // port		TCP port number (defaults to 119)
    // user		username (optional)
    // pass		password (optional)
    // area		subboard (internal code) newsgroup flags [attachment_dir]
    // ...
    
    // Defined area flags:
    // x		do not add tearlines & taglines to exported messages
    // n		do not add "From Newsgroup" text to imported messages
    // t		do not add tearline to imported messages
    // a		convert extended-ASCII chars to ASCII on imported messages
    // r		remove "Newsgroups:" header field from imported messages
    // o		over-write "Newsgroups:" header field for exported messages
    // b        decode binary attachments
    // i		import all (not just new articles)
    // s		no subject filtering
    // m		Moderate imported messages
    
    const REVISION = "1.2";
    
    printf("Synchronet NewsLink %s session started\r\n", REVISION);
    
    var tearline = format("--- Synchronet %s%s-%s NewsLink %s\r\n"
    					  ,system.version,system.revision,system.platform,REVISION);
    var tagline	=  format(" *  %s - %s - telnet://%s\r\n"
    					  ,system.name,system.location,system.inetaddr);
    var antispam = format(".remove-%s-this"
    					  ,random(50000).toString(36));
    
    var cfg_fname = system.ctrl_dir + "newslink.cfg";
    
    var global_flags = "";	// global flags  - area flags applied to all areas
    
    load("sbbsdefs.js");
    load("newsutil.js");	// write_news_header() and parse_news_header()
    
    var debug = false;
    var slave = false;
    var reader_mode = false;
    var reset_import_ptrs = false;		// Reset import pointers, import all messages
    var update_import_ptrs = false;		// Update import pointers, don't import anything
    var reset_export_ptrs = false;		// Reset export pointers, export all messages
    var update_export_ptrs = false;		// Update export pointers, don't export anything
    var email_addresses = true;			// Include e-mail addresses in headers
    var import_amount = 0;				// Import a fixed number of messages per group
    var lines_per_yield = 5;			// Release time-slices ever x number of lines
    var yield_length = 1;				// Length of yield (in milliseconds)
    var max_newsgroups_per_article = 5;	// Used for spam-detection
    var unmangle = false;
    var use_xover = true;
    var connect_timeout = 30;
    
    // Parse arguments
    for(i=0;i<argc;i++) {
    	if(argv[i].toLowerCase()=="-d")			// debug
    		debug = true;
    	else if(argv[i].toLowerCase()=="-ri")	// reset import pointers (import all)
    		reset_import_ptrs = true;
    	else if(argv[i].toLowerCase()=="-ui")	// update import pointers (import none)
    		update_import_ptrs = true;
    	else if(argv[i].toLowerCase()=="-re")	// reset export pointers (export all)
    		reset_export_ptrs = true;
    	else if(argv[i].toLowerCase()=="-ue")	// update export pointers (export none)
    		update_export_ptrs = true;
    	else if(argv[i].toLowerCase()=="-ne")	// no e-mail addresses
    		email_addresses = false;
    	else if(argv[i].toLowerCase()=="-nm")	// no mangling of e-mail addresses
    		antispam = "";
    	else if(argv[i].toLowerCase()=="-um")	// un-mangle e-mail addresses when importing
    		unmangle = true;
    	else if(argv[i].toLowerCase()=="-nx")	// no xover command when importing
    		use_xover = false;
    	else if(argv[i].toLowerCase()=="-ix") 	// import a fixed number of messages
    	{
    		import_amount = parseInt(argv[i+1]);
    		if(import_amount)
    			i++;
    		else
    			import_amount = 500;			// default 500
    	}
    	else
    		cfg_fname = argv[i];
    }
    
    if(this.js==undefined) 		// v3.10?
    	js = { terminated: false };
    else
    	js.auto_terminate = false;
    
    // Write a string to the server socket
    function write(str)
    {
    	socket.send(str);
    }
    
    function writeln(str)
    {
    	if(debug)
    		printf("cmd: %s\r\n",str);
    	write(str + "\r\n");
    }
    
    // RFC977 and RFC3977 clearly state 512-bytes per response line,
    // but "Timelord" reported a problem with some news server sending
    // > 512-byte response lines for the XOVER command
    function readln()
    {
    	rsp = socket.recvline(4096);
    	if(debug)
    		printf("rsp: %s\r\n",rsp);
    	return(rsp);
    }
    
    function unique_fname(dir,fname)
    {
    	var new_fname=fname;
    	var file_num=0;
    	var ext;
    	while(file_exists(dir + new_fname) && file_num<1000) { 
    		// generate unique name, if necessary
    		ext=fname.lastIndexOf('.');
    		if(ext<0)
    			ext="";
    		else
    			ext=fname.slice(ext);
    		// Convert filename.ext to filename.<article>.ext
    		new_fname=format("%.*s.%lu%s",fname.length-ext.length,fname,file_num++,ext);
    	}
    	return(dir + new_fname);
    }
    
    // Remove NewsLink anti-spam meature from e-mail address
    function unmangle_addr(addr)
    {
    	return addr.replace(/\.remove-\S+-this/,"");
    }
    
    var yield_calls=0;
    function maybe_yield()
    {
    	yield_calls++;
    	if(lines_per_yield && (yield_calls%lines_per_yield)==0) {
    		sleep(yield_length);
    	}
    }
    
    var host;
    var port=119;
    var username;
    var password;
    var interface_ip_address=0;
    var port_set=false;
    var tls=false;
    var no_path=false;
    var approved;
    var area = {};
    
    if(this.server!=undefined)
    	interface_ip_address=server.interface_ip_address;
    
    /******************************/
    /* Read/Parse the Config File */
    /******************************/
    
    cfg_file = new File(cfg_fname);
    if(!cfg_file.open("r")) {
    	printf("!Error %d opening %s\r\n",errno,cfg_fname);
    	delete cfg_file;
    	exit(1);
    }
    
    while(!cfg_file.eof) {
    	line = cfg_file.readln();
    	if(line == null)
    		continue;
    	line = truncsp(line);
    	if(line==null || line[0] == ';' || !line.length || line==undefined)
    		continue;
    	str=line.split(/\s+/);
    	switch(str[0].toLowerCase()) {
    		case "disabled":
    			printf("!NewsLink DISABLED in %s\r\n",cfg_fname);
    			delete cfg_file;
    			exit();
    		case "server":
    			host=str[1];
    			break;
    		case "tls":
    			tls=true;
    			if(!port_set)
    				port=563;
    			break;
    		case "port":
    			port=parseInt(str[1]);
    			port_set=true;
    			break;
    		case "interface":
    			interface_ip_address=str[1];
    			break;
    		case "user":
    			username=str[1];
    			break;
    		case "pass":
    			password=str[1];
    			break;
    		case "flags":
    			global_flags=str[1];
    			break;
    		case "area":
    			area[str[1]] = { newsgroup: str[2], flags: str[3], attachment_dir: str[4] };
    			break;
    		case "auto_areas":
    		{
    			for(var s in msg_area.sub)
    				if((msg_area.sub[s].settings & SUB_INET) && !area[s])
    					area[s] = {};
    			break;
    		}
    		case "debug":
    			debug=true;
    			break;
    		case "unmangle":
    			unmangle=true;
    			break;
    		case "no_xover":
    			use_xover=false;
    			break;
    		case "no_path":
    			no_path=true;
    			break;
    		case "slave":
    			slave=true;
    			break;
    		case "reader_mode":
    			reader_mode = true;
    			break;
    		case "tagline":
    			str.shift();			// Remove first element (keyword)
    			tagline=str.join(' ');		// Combine remaining elements (tagline)
    			tagline+="\r\n";
    			break;
    		case "lines_per_yield":
    			lines_per_yield=parseInt(str[1]);
    			break;
    		case "yield_length":
    			yield_length=parseInt(str[1]);
    			break;
    		case "max_newsgroups_per_article":
    			max_newsgroups_per_article=parseInt(str[1]);
    			break;
    		case "approved":
    			approved = str[1];
    			break;
    		default:
    			print("!UNRECOGNIZED configuration keyword: " + str[0]);
    			break;
    	}
    }
    cfg_file.close();
    delete cfg_file;
    
    printf("server: %s\r\n",host);
    if(debug) {
    	printf("username: %s\r\n",username);
    	printf("password: %s\r\n",password);
    }
    
    if(host==undefined || !host.length) {
    	print("!No news server specified");
    	exit(1);
    }
    
    printf("Connecting to %s port %d ...\r\n",host,port);
    socket = new Socket();
    //socket.debug=true;
    socket.bind(0,interface_ip_address);
    if(!socket.connect(host, port, connect_timeout)) {
    	printf("!Error %d connecting to %s port %d\r\n"
    		,socket.last_error,host,port);
    	delete socket;
    	exit(1);
    }
    print("Connected");
    if(tls) {
    	print("Negotiating TLS");
    	socket.ssl_session=true;
    }
    readln();
    
    if(reader_mode) {
    	writeln("MODE READER");
    	readln();
    }
    
    if(username!=undefined && username.length) {
    	print("Authenticating...");
    	writeln(format("AUTHINFO USER %s",username));
    	readln();
    	if(password!=undefined && password.length) {
    		writeln(format("AUTHINFO PASS %s",password));
    		rsp = readln();
    		if(rsp==null || rsp[0]!='2') {
    			printf("!Authentication FAILURE: %s\r\n", rsp);
    			delete socket;
    			exit(1);
    		}
    	}
    	print("Authenticated");
    }
    
    if(slave) {
    	writeln("slave");
    	readln();
    }
    
    var stop_semaphore=system.data_dir+"newslink.stop";
    file_remove(stop_semaphore);
    
    function stop_sem_signaled()
    {
    	if(file_exists(stop_semaphore)) {
    		log(stop_semaphore + " signaled");
    		return(true);
    	}
    	return(false);
    }
    
    /******************************/
    /* Export and Import Messages */
    /******************************/
    var exported=0;
    var imported=0;
    var subimported=0;
    var subpending=0;
    var twitlist = [];
    
    var f = new File(system.ctrl_dir + "twitlist.cfg");
    if(f.open("r")) {
    	twitlist = f.readAll();
    	f.close();
    }
    
    function article_listed(list, article)
    {
    	var i;
    
    	for(i in list)
    		if(list[i]==article)
    			return(list.splice(i,1));
    
    	return(false);
    }
    
    function save_ptr(ini_file, name, value)
    {
    	/* Save Pointers */
    	if(!ini_file.open(ini_file.exists ? "r+":"w+"))
    		printf("!ERROR %d creating/opening %s\r\n",errno,ini_file.name);
    	else {
    		ini_file.iniSetValue("NewsLink", name, value);
    		ini_file.close();
    	}
    }
    
    printf("Scanning message bases...\r\n");
    for(sub in area) {
    
    	if(!socket.is_connected)
    		break;
    
    	if(js.terminated || stop_sem_signaled())
    		break;
    
    	newsgroup = area[sub].newsgroup;
    	flags = area[sub].flags;
    	if(flags==undefined)
    		flags="";
    	flags += global_flags;
    	flags = flags.toLowerCase();
    
    	msgbase = new MsgBase(sub);
    	if(msgbase.open!=undefined && msgbase.open()==false) {
    		printf("!ERROR %s opening msgbase: %s\r\n",msgbase.last_error,sub);
    		delete msgbase;
    		continue;
    	}
    
    	// Use default newsgroup name if not configured
    	if(newsgroup==undefined || newsgroup.length<2)
    		newsgroup=msgbase.cfg.newsgroup;
    
    	attachment_dir=area[sub].attachment_dir;
    	if(attachment_dir==undefined)
    		attachment_dir=msgbase.cfg.data_dir+"attach";
    	mkdir(attachment_dir);   
    	if(attachment_dir.substr(-1)!='/')
    		attachment_dir+="/";   
    
    	printf("sub: %s, newsgroup: %s\r\n",sub,newsgroup);
    
    	/*********************/
    	/* Read Pointer File */
    	/*********************/
    	export_ptr = 0;
    	import_ptr = NaN;			// Set to highest possible message number (by default)
    	if(flags.indexOf('i')>=0)	// import all
    		import_ptr = 0;
    
    	ini_fname = msgbase.file + ".ini";
    	ini_file = new File(ini_fname);
    	if(ini_file.open("r")) {
    		export_ptr=ini_file.iniGetValue("NewsLink","export_ptr",export_ptr);
    		printf("%s export ptr: %ld\r\n",file_getname(ini_fname), export_ptr);
    		import_ptr=ini_file.iniGetValue("NewsLink","import_ptr",import_ptr);
    		printf("%s import ptr: %ld\r\n",file_getname(ini_fname),import_ptr);
    		ini_file.close();
    	}
    
    	if(reset_export_ptrs)
    		ptr = 0;
    	else if(update_export_ptrs)
    		ptr = msgbase.last_msg;
    	else 
    		ptr = export_ptr;
    
    	if(ptr < msgbase.first_msg)
    		ptr = msgbase.first_msg;
    	else
    		ptr++;
    
    	/*************************/
    	/* EXPORT Local Messages */
    	/*************************/
    	if(debug)
    		print("exporting local messages");
    	last_msg=msgbase.last_msg;
    	for(;socket.is_connected 
    		&& ptr<=last_msg 
    		&& !js.terminated
    		&& !stop_sem_signaled()
    		;ptr++) {
    		if(this.console!=undefined)
    			console.line_counter = 0;
    		hdr = msgbase.get_msg_header(
    			/* retrieve by offset? */	false,
    			/* message number */		ptr
    			);
    		if(hdr == null) {
    			if(msgbase.status != 0)
    				alert("Error (" + msgbase.error + ") reading msg header #" + ptr);
    			continue;
    		}
    		if(!hdr.id) {
    			alert("Message #" + ptr + " is missing a Message-ID header field");
    			continue;
    		}
    		if(hdr.attr&MSG_DELETE)	{ /* marked for deletion */
    			if(debug)
    				printf("skipping deleting msg #" + ptr);
    			continue;
    		}
    		if(hdr.attr&MSG_MODERATED && !(hdr.attr&MSG_VALIDATED)) {
    			print("Stopping at unvalidated moderated message: " + ptr);
    			ptr--;
    			break;
    		}
    		if(hdr.attr&MSG_PRIVATE) { /* no private messages on NNTP */
    			if(debug)
    				printf("skipping deleted msg #" + ptr);
    			continue;
    		}
    		if(hdr.from_net_type==NET_INTERNET)	{ /* no dupe loop */
    			if(debug)
    				printf("skipping msg from the Internet #" + ptr);
    			continue;
    		}
    		if(hdr.from_net_type	/* don't gate messages between net types */
    			&& msgbase.cfg!=undefined && !(msgbase.cfg.settings&SUB_GATE)) {
    			if(debug)
    				printf("skipping (not gating) networked message #" + ptr);
    			continue;
    		}
    
    		body = msgbase.get_msg_body(
    				 false	/* retrieve by offset */
    				,ptr	/* message number */
    				,true	/* remove ctrl-a codes */
    				,true	/* rfc822 formatted text */
    				,true	/* include tails */
    				);
    		if(body == null) {
    			printf("!FAILED to read message number %ld\r\n",ptr);
    			continue;
    		}
    		if(msgbase.cfg!=undefined && msgbase.cfg.settings&SUB_ASCII) {
    			/* Convert Ex-ASCII chars to approximate ASCII equivalents */
    			body = ascii_str(body);
    			hdr.subject = ascii_str(hdr.subject);
    		}
    		if(flags.indexOf('x')==-1) {
    			body += tearline;
    			body += tagline;
    		}
    		if(0) 
    			writeln(format("IHAVE %s",hdr.id));
    		else
    			writeln("POST");
    
    		rsp = readln();
    		if(rsp==null || rsp[0]!='3') {
    			printf("!POST FAILURE: %s\r\n",rsp);
    			break;
    		}
    
    		if(!email_addresses)
    			writeln(format("From: %s@%s",hdr.from,newsgroup));
    		else if(hdr.from.indexOf('@')!=-1)
    			writeln(format("From: %s%s",hdr.from,antispam));
    		else if(hdr.from_net_type && hdr.from_net_addr!=null) {
    			if(hdr.from_net_addr.indexOf('@')!=-1)
    				writeln(format("From: \"%s\" <%s%s>"
    					,hdr.from
    					,hdr.from_net_addr,antispam));
    			else
    				writeln(format("From: \"%s\" <%s@%s%s>"
    					,hdr.from
    					,hdr.from.replace(/ /g,".").toLowerCase()
    					,hdr.from_net_addr,antispam));
    		}
    		else
    			writeln(format("From: \"%s\" <%s@%s%s>"
    				,hdr.from
    				,hdr.from.replace(/ /g,".").toLowerCase()
    				,system.inetaddr,antispam));
    
    		if(hdr.newsgroups==undefined || flags.indexOf('o')>=0)
    			hdr.newsgroups=newsgroup;
    
    		if(hdr.from_org==undefined && !hdr.from_net_type)
    			hdr.from_org=system.name;
    
    		write_news_header(hdr,writeln); // from newsutil.js
    
    		writeln("X-Gateway: "
    			+ system.inetaddr
    			+ " [Synchronet "
    			+ system.version + system.revision 
    			+ "-" + system.platform + system.beta_version
    			+ " NewsLink " + REVISION
    			+ "]"
    			);
    
    		if(approved)
    			writeln("Approved: " + approved);
    
    		writeln("");
    		if(hdr.to.toLowerCase()!="all") {
    			writeln("  To: " + hdr.to);
    		}
    		write(body);
    		writeln(".");
    		rsp = readln();
    		if(rsp==null || rsp[0]!='2') {
    			printf("!POST FAILURE: %s\r\n",rsp);
    			break;
    		}
    		printf("Exported message %lu to newsgroup: %s\r\n",ptr,newsgroup);
    		exported++;
    	}
    	if(ptr > msgbase.last_msg)
    		ptr = msgbase.last_msg;
    	export_ptr = ptr;
    	save_ptr(ini_file, "export_ptr", export_ptr);
    
    	if(stop_sem_signaled())
    		break;
    
    	/***************************/
    	/* IMPORT Network Messages */
    	/***************************/	
    
    	writeln(format("GROUP %s",newsgroup));
    	rsp = readln();
    	if(rsp==null || rsp[0]!='2') {
    		printf("!GROUP %s FAILURE: %s\r\n",newsgroup,rsp);
    		delete ini_file;
    		delete msgbase;
    		continue;
    	}
    	str = rsp.split(' ');
    
    	first_msg = parseInt(str[2]);
    	last_msg = parseInt(str[3]);
    
    	if(reset_import_ptrs)
    		ptr = 0;
    	else if(update_import_ptrs)
    		ptr = last_msg;
    	else if(import_amount)
    		ptr = last_msg - import_amount;
    	else
    		ptr = import_ptr;
    
    	printf("%s import ptr: %ld, first_msg: %ld, last_msg: %ld\r\n"
    		,newsgroup,ptr,first_msg,last_msg);
    
    	if(ptr < first_msg)
    		ptr = first_msg;
    	else {
    		if(ptr > last_msg || isNaN(ptr))
    			ptr = last_msg;
    		ptr++;
    	}
    	if(msgbase.cfg.max_msgs > 0 && last_msg - ptr > msgbase.cfg.max_msgs)
    		ptr = (last_msg - msgbase.cfg.max_msgs) + 1;
    
    	delete article_list;
    	subpending=0;
    	if(use_xover && ptr<=last_msg) {
    		writeln(format("XOVER %u-%u", ptr, last_msg));
    		if(parseInt(readln())==224) {
    			printf("Getting headers for articles %u through %u\r\n", ptr, last_msg);
    			article_list = new Array();
    			while((rsp=readln())!='.' && socket.is_connected && !js.terminated) {
    				if(rsp)
    					article_list.push(parseInt(rsp));
    				maybe_yield();
    			}
    			printf("%u new articles\r\n", article_list.length);
    			subpending=article_list.length;
    		}
    	}
    
    	subimported=0;
    	for(;socket.is_connected 
    		&& ptr<=last_msg 
    		&& !js.terminated
    		&& !stop_sem_signaled()
    		;ptr++) {
    		if(this.console!=undefined)
    			console.line_counter = 0;
    
    		if(this.article_list!=undefined && !article_listed(article_list,ptr)) {
    			subpending--;
    			continue;
    		}
    
    		printf("Retrieving %s article: %u\r\n", newsgroup, ptr);
    		writeln(format("ARTICLE %lu",ptr));
    		rsp = readln();
    		if(rsp==null || rsp[0]!='2') {
    			printf("!ARTICLE %lu ERROR: %s\r\n",ptr,rsp);
    			subpending--;
    			continue;
    		}
    		body="";
    		header=true;
    		var line;
    		var line_counter=0;
    		var recv_lines=0;
            var file=undefined;   
    		var hfields=[];
            var md5; 
    		while(socket.is_connected && !js.terminated) {
    
    			maybe_yield();
    
    			line = readln();
    
    			if(line==null) {
    				print("!TIMEOUT waiting for text line\r\n");
    				break;
    			}
    
    			recv_lines++;
    
    			if(debug)
    				printf("msgtxt: %s\r\n",line);
    
    			if(line==".") {
    				if(debug)
    					print("End of message text");
    				break;
    			}
    			if(line=="" && header) {
    				header=false;
    				continue;
    			}
    
    			if(header) {
    				if((line.charAt(0)==' ' || line.charAt(0)=='\t') && hfields.length) {
    					while(line.charAt(0)==' '	// trim prepended spaces
    						|| line.charAt(0)=='\t')	
    						line=line.slice(1);
    					hfields[hfields.length-1] += line;	// folded header field
    				} else
    					hfields.push(line);
    				continue;
    			}
    
    			/* Body text, append to 'body' */
    			if(line.charAt(0)=='.')
    				line=line.slice(1);		// Skip prepended dots
    
    			body += line+"\r\n";
    			line_counter++;
    
                if(flags.indexOf('b')>=0) {        // decode attachments
    				if(file==undefined
    					&& line.substr(0,8)=="=ybegin " 
    					&& line.indexOf(" line=")>0
    					&& (size=line.indexOf(" size="))>0
    					&& (name=line.indexOf(" name="))>0) {
    
    					printf("yenc header: %s\r\n",line);
    
    					size=parseInt(line.slice(size+6),10);
    					fname=file_getname(line.slice(name+6));
    
    					if((total=line.indexOf(" total="))>0)
    						total=parseInt(line.slice(total+7),10);
    
    					if(total>1)	/* multipart */
    						continue;
    
    					/* part? */
    					if((part=line.indexOf(" part="))>0)
    						part=parseInt(line.slice(part+6),10);
    
    					if(part>1) /* multipart */
    						continue;
    
    					if(part>0) {
    
    						line = readln();
    
    						body += line+"\r\n";
    
    						printf("ypart header: %s\r\n",line);
    
    						if(line.substr(0,7)!="=ypart "
    							|| (begin=line.indexOf(" begin="))<1
    							|| (end=line.indexOf(" end="))<1) {
    							printf("!yEnc MALFORMED ypart line: %s\r\n",line);
    							continue;
    						}
    						if(begin!=1 || end!=size)	/* multipart */
    							continue;
    					}
    
    					fname=unique_fname(attachment_dir,fname);
                        file=new File(fname);   
                        file.yenc=true;   
                        if(file.open("w+b"))   
                            printf("Receiving/decoding attachment: %s\r\n",file.name);
                        else   
                            printf("!ERROR %s opening %s\r\n",errno_str,file.name);
                        continue;   
    				}	
    
                    if(file==undefined && line.substr(0,6)=="begin ") {   
    					 // Parse uuencode header   
    					 arg=line.split(/\s+/);   
    					 arg.splice(0,2);        // strip "begin 666 "   
    					 fname=file_getname(arg.join(" "));   
    					 fname=unique_fname(attachment_dir,fname);    
                         file=new File(fname);   
                         file.uue=true;   
                         if(file.open("w+b"))   
                             printf("Receiving/decoding attachment: %s\r\n",file.name);   
                         else   
                             printf("!ERROR %s opening %s\r\n",errno_str,file.name);
                         continue;   
                     }    
                     if(file!=undefined && file.is_open==true) {   
                        if(file.uue && line=="end") {   
                             md5=file.md5_hex;   
                             file.close();
    						 continue;
    					}
    					if(file.yenc && line.substr(0,6)=="=yend "
    						&& (part_size=line.indexOf(" size="))>0) {
    						part_size=parseInt(line.slice(part_size+6),10);
    						if(part_size!=size)	{ /* multi-part, ignore */
    							file.remove();
    							delete file;
    							continue;
    						}
    						if((crc32=line.indexOf(" pcrc32="))>0)
    							crc32=parseInt(line.slice(crc32+8),16);
    						else if((crc32=line.indexOf(" crc32="))>0)
    							crc32=parseInt(line.slice(crc32+7),16);
    						else
    							crc32=undefined;
    
    						if(crc32!=undefined) {
    							file_crc32=file.crc32;
    							if(crc32!=file_crc32) {
    								printf("!yEnc CRC-32 mismatch, actual: %08lX, trailer: %s\r\n"
    									,file_crc32,line);
    								file.remove();
    								delete file;
    								continue;
    							}
    						}
    
    						md5=file.md5_hex;
    						file.close();
    						continue;
    					}
    
    					if(!file.write(line))   
    						printf("!ERROR decoding/writing: %s\r\n",line);   
    				}
    			}
    			//print(line);
    		}
    
    		if(debug)
    			print("Parsing message header");
    		// Parse the message header
    		var hdr={ from: "", to: newsgroup, subject: "", id: "" };
    		for(h in hfields)
    			parse_news_header(hdr,hfields[h]);	// from newsutil.js
    
    		if(hdr.extra_headers!=undefined
                && hdr.extra_headers["nntp-posting-host"]!=undefined 
    			&& (system.trashcan("ip", hdr.extra_headers["nntp-posting-host"]) 
    				|| system.trashcan("ip-silent", hdr.extra_headers["nntp-posting-host"]))) {
    			print("!Filtered IP address in NNTP-Posting-Host header field: " + hdr.extra_headers["nntp-posting-host"]);
    			subpending--;
    			continue;
    		}
    
            if(file!=undefined) {   
    			if(debug)
    				print("Parsing attachment");
    
                if(file.is_open==true) { /* Incomplete attachment? */
    				print("!Incomplete attachment: " + file_getname(file.name));
                    file.remove();
                } else {
    				/* Search for duplicate MD5 */
    				print("Searching for duplicate file");
    				var duplicate=false;
    				md5_file=new File(attachment_dir + "md5.lst");
    				if(md5_file.open("r")) {
    					while(!md5_file.eof && !duplicate) {
    						str=md5_file.readln();
    						if(str==null)
    							break;
    						if(str.substr(0,32)==md5)
    							duplicate=true;
    					}
    					md5_file.close();
    				}
    				if(duplicate) {   
    					printf("Duplicate MD5 digest found: %s\r\n",str);   
    					if(file_remove(file.name))   
    						printf("Duplicate file removed: %s\r\n",file.name);   
    					else   
    						printf("!ERROR removing duplicate file: %s\r\n",file.name);   
    					subpending--;
    					continue;   
    				}
    				/* Append MD5 to history file */
    				if(md5_file.open("a")) {
    					md5_file.printf("%s %s\n",md5,file_getname(file.name));
    					md5_file.close();
    				} else
    					printf("!ERROR %d (%s) creating/appending %s\r\n"
    						,errno, errno_str, md5_file.name);
    				subpending--;
    				continue;
    			}
            } 
    
    		if(debug)
    			print("Decoding body text");
    		
    		body=decode_news_body(hdr, body);
    
    		if(truncsp(body).length==0) {
    			printf("Message %lu not imported (blank)\r\n",ptr);
    			subpending--;
    			continue;
    		}
    
    		if(max_newsgroups_per_article && hdr.newsgroups!=undefined) {
    			var ngarray=hdr.newsgroups.split(',');
    			if(ngarray.length>max_newsgroups_per_article) {
    				var reason = format("Too many newsgroups (%d): %s",ngarray.length, hdr.newsgroups);
    				printf("!%s\r\n",reason);
    				system.spamlog("NNTP","NOT IMPORTED",reason,hdr.from,host,hdr.to);
    				subpending--;
    				continue;
    			}
    		}
    
    		if(hdr.to==newsgroup && hdr.newsgroups!=undefined)
    			hdr.to=hdr.newsgroups;
    
    		// Duplicate/looped message detection here
    		if(hdr.id.indexOf('@' + system.inetaddr)!=-1) {
    			log(LOG_DEBUG, "Duplicate/looped message-ID: " + hdr.id);
    			subpending--;
    			continue;
    		}
    		if(hdr.path
    			&& hdr.path.indexOf(system.inetaddr)!=-1) {
    			log(LOG_DEBUG, "Duplicate/looped message path: " + hdr.path);
    			subpending--;
    			continue;
    		}
            if(no_path && hdr.path)
                delete hdr.path;
    
    		if(hdr.gateway
    			&& hdr.gateway.indexOf(system.inetaddr)!=-1) {
    			log(LOG_DEBUG, "Duplicate/looped message gateway: " + hdr.gateway);
    			subpending--;
    			continue;
    		}
    
    		if(flags.indexOf('s')==-1 && system.trashcan("subject",hdr.subject)) {
    			var reason = format("Blocked subject (%s)",hdr.subject);
    			printf("!%s\r\n",reason);
    			system.spamlog("NNTP","NOT IMPORTED",reason,hdr.from,host,hdr.to);
    			subpending--;
    			continue;
    		}
    		if(twitlist.length
    			&& (system.findstr(twitlist,hdr.from)
    				|| system.findstr(twitlist,hdr.from_net_addr)
    				|| system.findstr(twitlist,hdr.to))) {
    			printf("Filtering message from %s (%s) to %s\r\n", hdr.from, hdr.from_net_addr, hdr.to);
    			subpending--;
    			continue;
    		}
    
    		if(flags.indexOf('n')==-1)
    			body=format("\1n\1b\1hFrom Newsgroup\1n\1b: \1h\1c%s\1n\r\n\r\n",newsgroup) + body;
    
    		if(flags.indexOf('a')>=0) {	// import ASCII only (convert ex-ASCII to ASCII)
    			body = ascii_str(body);
    			hdr.subject = ascii_str(hdr.subject);
    		}
    		if(flags.indexOf('r')>=0) 	// remove "Newsgroups:" header field
    			delete hdr.newsgroups;
    
    		if(hdr.from_net_addr && unmangle)
    			hdr.from_net_addr = unmangle_addr(hdr.from_net_addr);
    
    		hdr.from_net_type=NET_INTERNET;
    //		hdr.from_net_addr=hdr.from;
    		if(flags.indexOf('t')==-1)
    			body += tearline;
    		if(flags.indexOf('m')>=0)
    			hdr.attr |= MSG_MODERATED;
    
    		if(debug)
    			print("Saving message");
    
    		if(msgbase.save_msg(hdr,body)) {
    			imported++;
    			subimported++;
    			printf("Message %lu imported into %s (%lu of %lu total) %lu lines\r\n"
    				,ptr, sub, subimported, subpending, line_counter);
    		} else {
    			printf("!IMPORT %lu ERROR: %s\r\n", ptr, msgbase.last_error);
    			subpending--;
    		}
    	}
    
    	ptr--;	/* point to last requested article number */
    	if(ptr > last_msg)
    		ptr = last_msg;
    	import_ptr = ptr;
    
    	save_ptr(ini_file, "import_ptr", import_ptr);
    	delete ini_file;
    	msgbase.close();
    	delete msgbase;
    
    //	if(flags.indexOf('b')>=0)	// binary newsgroup
    //		load("binarydecoder.js",sub);
    }
    
    if(!socket.is_connected)
    	print("!DISCONNECTED BY SERVER");
    else {
    	writeln("quit");
    	readln();
    }
    
    delete socket;
    
    printf("Synchronet NewsLink %s session complete (%lu exported, %lu imported)\r\n"
    	   ,REVISION, exported, imported);
    
    /* End of newslink.js */