//#pragma save_binary // File : /adm/daemons/network/nntp.c // Creator : Leto@Earth (Earth Meister) [29/03/94] // Drizzt@TMI-2 (Socket Meister) [01/04/95] // #include <priv.h> #include <uid.h> #include <config.h> #include <net/nntp.h> #include <net/socket.h> #include <net/daemons.h> #include <net/macros.h> #include <mudlib.h> inherit DAEMON ; #define DEBUG(x) tell_object(find_player("leto"),"NNTP:"+x) int mysocket; mapping sockets; /* Retrieval prototypes */ string get_article(string message_id); string get_head(string message_id); string get_body(string message_id); int check_article(string message_id); string nntp_time(int mudtime); void nntp_post(mapping msg,string board_name, string poster); /* Networking prototypes */ void read_callback(int fd, string str); void close_callback(int fd); void write_callback(int fd); void listen_callback(int fd); void write_callback(int fd); /* NNTP commands prototypes */ void cmd_list(int fd, string rest); void cmd_quit(int fd); void cmd_get(int fd, string rest); void cmd_next(int fd); void cmd_stat(int fd, string rest); void cmd_body(int fd, string rest); void cmd_head(int fd, string rest); void cmd_help(int fd); void cmd_slave(int fd); void cmd_newnews(int fd, string parsedate); void cmd_article(int fd, string mesg_id); void cmd_ihave(int fd, string mesg_id); /* -=] Begin Article Manipulation Section [=- */ /* Name: get_article() Purpose: Returns an article with given id, and newsgroup */ string get_article(string message_id) { string article; DEBUG("IN GET ARTICLE"); // Just in case, let's check if we have one. if (check_article(message_id)) { DEBUG("file:"+NNTP_DIR+"/"+message_id); article = read_file(NNTP_DIR+"/"+message_id); return article; } else return("999 Unknown message-id or group.\n"); // 999 is wrong // You shouldn't ask for something that's not there } /* Name: check_article() Purpose: To check for an existance of an article */ int check_article(string message_id) { // Check to see if we have a certain article. // To reject a duplicate article from an nntp server // first check if we have that newsgroup DEBUG("IN CHECKARTICLE:group="); if (!directory_exists(NNTP_DIR)){ // Looks like we dont have it DEBUG("newsgroup DOESN'T exist:\n"); DEBUG(NNTP_DIR); return 0; } else if (file_exists(NNTP_DIR+"/"+message_id)) { DEBUG("article exists\n"); return 1; } else return 0; // group ok, no such article though } /* -=] Begin NNTP utilities Section [=- */ /* Name: nntp_time() Purpose: To make a time format suitable for NNTP usage */ string nntp_time(int mudtime){ string wrong,day,monthname,month,time,year; wrong = ctime(mudtime); sscanf(wrong,"%s %s %s %s %s",day,monthname,month,time,year); if (!month) { sscanf(wrong,"%s %s %s %s %s",day,monthname,month,time,year); } return (day+", "+month+" "+monthname+" "+year+" "+time+" "+TIME_ZONE); } /* Name: nntp_post() Purpose: Write an article to file Use RFC822, and RFC950 */ void nntp_post(mapping msg, string board_name, string poster) { string NNTP_OUT; DEBUG("Intermud posting:"+NNTP_DIR + board_name+"/"+(string)msg["id_long"]); NNTP_OUT = NNTP_DIR + board_name+"/"+(string)msg["id_long"]; write_file(NNTP_OUT,"Path: "+"@"+capitalize(MUD_NAME)+".org!"+poster+"\n"); write_file(NNTP_OUT,"From: "+msg["poster"]+".org\n"); write_file(NNTP_OUT,"Newsgroups: "+board_name+"\n"); write_file(NNTP_OUT,"Subject: "+msg["title"]+"\n"); write_file(NNTP_OUT,"Message-ID: "+msg["id_long"]+"\n"); write_file(NNTP_OUT,"Date: "+NNTP_D->nntp_time(msg["time"])+"\n"); write_file(NNTP_OUT,"Reply-to: "+NNTP_ACCOUNT+ "\n"); write_file(NNTP_OUT,"Distribution: "+ "World"+"\n"); write_file(NNTP_OUT,"Organisation: A mud called "+capitalize(MUD_NAME)+"\n"); write_file(NNTP_OUT,"Comment: For problems contact "+ADMIN_EMAIL+"\n"); write_file(NNTP_OUT,"NNTP-POSTING-HOST: "+"LPNNTP version " + NNTP_VERSION + " at "+ capitalize(MUD_NAME)+"\n"); write_file(NNTP_OUT,"\n"); write_file(NNTP_OUT,msg["body"]); write_file(NNTP_OUT,".\n"); } /* -=] Begin Socket Section [=- */ /* Drizzt Note: For right now, we will not make this NM compatable.. #1, I do not Know NM code, and #2, they can do it themselves (or someone who wants to make a bi-compatable version (Leto) */ /* Name: create() Purpose: Setup sockets, etc. Initialize object variables */ create(){ int i; mysocket = 0; sockets = ([]); /* Sockets looks like: ([ fd : logintime ]) */ mysocket = socket_create(STREAM, "read_callback", "close_callback"); if(mysocket < 0){ log_file("nntpd", sprintf("%25-s: %s\n", "Create Error",socket_error(mysocket))); return 0; } i = socket_bind(mysocket, PORT_NNTP); if(i < 0){ socket_close(mysocket); log_file("nntpd",sprintf("%25-s: %s\n", "Bind Error",socket_error(i))); return 0; } i = socket_listen(mysocket, "listen_callback"); if(i < 0){ socket_close(mysocket); log_file("nntpd",sprintf("%25-s: %s\n", "Listen Error",socket_error(i))); return 0; } } /* Name: close_callback() Purpose: Closes a connection with a socket */ void close_callback(int fd) { log_file("nntpd", sprintf("%25-s: %23-s %s\n", "Close",socket_address(fd),(time() - sockets[fd])/60+" minutes logged")); socket_close(fd); map_delete(sockets, fd); } /* Name: listen_callback() Purpose: To listen for incoming connections, and send them on their way when they get here */ void listen_callback(int fd){ int socket; socket = socket_accept(fd, "read_callback", "write_callback"); if(socket < 0){ log_file("nntpd",sprintf("%25-s: %s\n", "Accept Error",socket_error(socket))); return; } sockets += ([ socket : time() ]); write_callback(socket); } /* Name: write_callback() Purpose: Dont really know the purpose of this in a "listening" object In clients its used to tell the object when its ok to write to the socket */ void write_callback(int fd){ socket_write(fd, "201 "+capitalize(MUD_NAME)+ "LPNNTP server ready (No Posting Yet [Kick Leto])\n"); log_file("nntpd",sprintf("%25-s: %s\n", "Accepted",socket_address(fd))); } /* Name: read_callback() Purpose: Called whenever the server receives information from the Client */ void read_callback(int fd, string str) { string cmd, rest, mesg_id, newsgroup; if(!str || str == "") { socket_write(fd, NNTP_BAD_CMD); return; } //str = replace_string(str,"\r","\n"); str = explode(replace_string(str, "\r", ""), "\n")[0]; DEBUG("NNTP:"+str+"\n"); sscanf(str, "%s %s", cmd, rest); if (!cmd || (cmd=="")) cmd = str; DEBUG("nntpcmd="+cmd+".\n"); DEBUG("nntprest="+rest+".\n"); switch(lower_case(cmd)) { case "quit": cmd_quit(fd);return; case "list": cmd_list(fd,rest); return; case "newnews": cmd_newnews(fd,rest); return; case "help": cmd_help(fd); return; case "article": cmd_article(fd, rest); return; case "head": cmd_head(fd, rest); return; case "body": cmd_body(fd, rest); return; case "ihave": cmd_ihave(fd, rest); return; case "stat": cmd_stat(fd, rest); return; case "next": cmd_next(fd); return; case "slave": cmd_slave(fd); return; default: socket_write(fd, "500 Command unrecognized.\n"); return; } } /* Name: cmd_next() Purpose: To return the next mesg_id header */ void cmd_next(int fd) { // this should display the next mesg_id header as follows // "223 <article#, local# to the server> <mesg_id> "+ // "Article retrieved; request text separately.\n" return ; } /* Name: cmd_body() Purpose: Return the body of an article */ void cmd_body(int fd, string rest) { if(!check_article(rest)) { socket_write(fd,"430 No article by "+rest+", sorry.\n"); return; } else { socket_write(fd, "222 0 "+rest+"Article retrieved, body follows.\n"); get_body(rest); return; } } /* Name: cmd_head() Purpose: Return the headers of an article */ void cmd_head(int fd, string rest) { if(!check_article(rest)) { socket_write(fd,"430 No article by "+rest+", sorry.\n"); return; } else { socket_write(fd, "221 0 "+rest+"Article retrieved, head follows.\n"); get_head(rest); return; } } /* Name: cmd_article Purpose: return both headers, and body of an article */ void cmd_article(int fd, string rest) { if(!check_article(rest)) { socket_write(fd,"430 No article by "+rest+", sorry.\n"); return; } else { socket_write(fd, "220 0 "+rest+"Article retrieved, head and body folow.\n"+ get_article(rest) ); return; } } /* Name: cmd_stat() Purpose: To retrieve an article, and possible ensure its existance */ void cmd_stat(int fd, string rest) { // return current mesg id if(!check_article(rest)) { socket_write(fd,"430 No article by "+rest+", sorry.\n"); return; } else { socket_write(fd, "203 0 "+rest+" Article retrieved, request text separately.\n"); return; } } /* Name: cmd_slave() Purpose: To write about leto's sex life :) */ void cmd_slave(int fd) { DEBUG("In slave cmd\n"); socket_write(fd, "202 Kinky, kinky. I don't support such perversions.\n"); return; } /* Name: cmd_help() Purpose: List help (probably for people telneting to server) */ void cmd_help(int fd) { socket_write(fd, "100 This server accepts the following commands:\n"+ "ARTICLE BODY GROUP\n"+ "HEAD LAST LIST\n"+ "NEXT POST QUIT\n"+ "STAT NEWGROUPS HELP\n"+ "IHAVE NEWNEWS SLAVE\n"+ "\n"+ "Send bug reports to Leto@Tmi-2 (earth@borg.xs4all.nl)\n"+ " Drizzt@Tmi-2 (jtravis@herbie.unl.edu)\n"+ ".\n"); return; } /* Name: cmd_list() Purpose: To list the NNTP groups currently available */ void cmd_list(int fd, string rest) { //if (!rest || rest=="") //socket_write(fd, implode(NNTP_GROUPS, "\n")); shout("dummy\n"); return; } /* Name: cmd_quit() Purpose: To disconnect the client from the server */ void cmd_quit(int fd) { socket_write(fd, "205 Thanks for using "+ capitalize(MUD_NAME) + "'s LPnntp, Goodbye\n" ); close_callback(fd); return; } void cmd_newnews(int fd, string parsedate) { int date,time; sscanf(parsedate,"%d %d",date,time); // format is ddmmyy hhmmss in two ints return; } /* void cmd_article(int fd, string mesg_id) { if(!check_article(mesg_id)) { socket_write(fd,"430 No article by "+mesg_id+", sorry.\n"); return; } else { socket_write(fd, // 221 is a wild guess, needs to be checked !! Leto "221 0 "+mesg_id+"Article retrieved, body follows.\n"+ get_article(mesg_id)); return; } } */ void cmd_ihave(int fd, string mesg_id) { //check existence of mesg_id, and ask article if not //here, else, tell em we don't want it return; }