// /adm/daemons/network/newsclient.c // Tim // News-reading client, usable by one person. Caches articles temporarily, // but doesn't save them. An automated (and better) client will be created later. // Also, it only connects to one mud. // Nov. 7, 2003 - started #include #include #include // Debugging #define DEB_IN 1 #define DEB_OUT 2 #define DEB_OTHER 0 // News modes #define NEWS_IDLE 0 #define NEWS_CONNECTING 1 #define NEWS_POSTING 2 #define NEWS_READING 3 #define NEWS_GROUPLIST 4 // Variables object news_socket; int mode; // what do we expect the server to send us? mapping news_cache; // holds articles that we've gotten object user; // person who is using this client (this is a one-person client) string current_group; // name of group you're reading int current_id; // number of article you are reading mapping group_info; // info about the groups which is told with news-grplist-req // example: ([ "code":({1,20}) ]) int group_update_time; // time when you last got a reply to a news-grplist-req // Prototypes void create(); void remove(); void check_news_socket(); void send_packet(mixed *data); void start(string site, int auth_num); void info(){ write(sprintf("news_socket: %s\nmode: %s\nnews_cache: %s\nuser: %s\ncurrent_group: %s\n" "current_id: %s\ngroup_info: %s\ngroup_update_time: %s\n",identify(news_socket), identify(mode),identify(news_cache),identify(user),identify(current_group), identify(current_id),identify(group_info),identify(group_update_time))); } varargs static private void debug(string message, int x){ if(find_player("tim")){ switch(x){ case DEB_IN : message("news","[%^MAGENTA%^NEWS-in%^RESET%^]: "+ (message)+"\n", find_player("tim")); break; case DEB_OUT : message("news","[%^CYAN%^NEWS-out%^RESET%^]: "+ (message)+"\n", find_player("tim")); break; case DEB_OTHER : default: message("news","[%^GREEN%^NEWS-info%^RESET%^]: "+ (message)+"\n", find_player("tim")); break; } message("news","mode="+mode+"\n",find_player("tim")); } } void read_callback(object socket, mixed info){ mixed mix; if(!sizeof(info)) return 0; log_file("news.data","SERVER: "+identify(info)+"\n"); debug(identify(info),DEB_IN); switch(mode){ case NEWS_CONNECTING: if((sizeof(info)!=4) || (info[0]!="oob-begin")){ if(user) message("news","The server responded with an invalid packet.\n",user); } else{ if(user) message("news","Connected to server "+info[1]+". Requesting list.\n",user); mode = NEWS_GROUPLIST; send_packet( ({ "news-grplist-req" }) ); } break; case NEWS_READING: // If we just sent a news-read-req, then we expect to get a response with // ({ posting_time, thread_id, subject, poster, contents }) // If an error reading, I guess they can instead send us // a ({ "error", error_msg }). if((sizeof(info)==2) && (info[0]=="error")){ if(user) message("news","Error reading: "+info[1]+"\n",user); mode = NEWS_IDLE; break; } if(sizeof(info)!=5){ debug("I'm trying to read, and didn't get the article?\n"); if(user) message("news", "The news server responded with an invalid packet.\n",user); mode = NEWS_IDLE; break; } if(!stringp(info[4])){ // If the contents is not a string, it's probably 0. // I'm just gonna send a packet with 5 0's to mean error. if(user) message("news","Article "+current_id+" in "+ current_group+" seems to be gone.",user); mode = NEWS_IDLE; break; } // cache this message news_cache[current_group][current_id] = info; // tell user that the article arrived if(user){ message("news","Article received: "+current_id+" in "+ current_group+"\n",user); mode = NEWS_IDLE; } break; case NEWS_POSTING: // If we just did a news-post-req then we expect an integer for // the id of the new article, or else maybe ({ "error", error_msg }) if((sizeof(info)==2) && (info[0]=="error")){ if(user) message("news","Error posting: "+info[1]+"\n",user); mode = NEWS_IDLE; break; } if(user) message("news","Message posted as article #"+info[0]+"\n",user); mode = NEWS_IDLE; break; case NEWS_GROUPLIST: message("news","got a grouplist reply...\n",user); // If we just did a news-grplist-req then we expect to get something // like: ({ ({"group1",1,10}) ({"group2",5,15}) }) if(!sizeof(info)){ if(user) message("news","No groups listed.\n",user); break; } foreach(mix in info){ if(!arrayp(mix) || sizeof(mix)!=3){ if(user) message("news", "Received invalid response to news-grplist-req\n",user); mode = NEWS_IDLE; break; } if(!group_info[mix[0]]){ // Group not previously known. if(user) message("news", sprintf("New group: %s [%d to %d]\n", mix[0], mix[1],mix[2]),user); news_cache[mix[0]] = ([ ]); // Initialize cache for this group. } else{ if(group_info[mix[0]][1]!=mix[2]){ // If we already knew about this group, and the // last_post_id is different now, then tell the user // that new articles are there. if(user) message("news","UPDATE: "+mix[0]+" has "+ (mix[2]-group_info[mix[0]][1])+ " new articles.\n",user); } } group_info[mix[0]]=({ mix[1], mix[2] }); mode = NEWS_IDLE; } if(user) message("news","Refreshed the group list.\n",user); mode = NEWS_IDLE; break; default: } return; } int close_callback(object socket){ log_file("news.data","DISCONNECT\n"); debug("Disconnected.", DEB_OTHER); socket->remove(); create(); return 1; } void send_packet(mixed *data){ debug(identify(data), DEB_OUT); log_file("news.data","CLIENT: "+identify(data)+"\n"); news_socket->send(data); } void create(){ seteuid(getuid()); log_file("news","Creating NEWS object at "+ctime(time())+".\n"); log_file("news.data","Creating NEWS object at "+ctime(time())+".\n"); news_cache = ([ ]); current_group = ""; current_id=1; group_info = ([ ]); group_update_time = 0; // start(0); user=find_player("tim"); start("192.168.0.20 4447", 0); } void start(string site, int auth_num){ debug("starting NEWS object"); log_file("news.data","Got auth "+auth_num+", Creating socket at "+ctime(time())+".\n"); mode = NEWS_CONNECTING; news_socket = clone_object(SOCKET, SKT_STYLE_CONNECT_M, site, (: read_callback :), (: close_callback :) ); // send_text("FLAPON\r\n\r"); // send_text("FLAPON\r\n\r\n"); if(auth_num) send_packet(({ "oob-begin", MUD_NAME, 1, auth_num })); else send_packet(({ "oob-begin", MUD_NAME, 0, 0 })); call_out( (: check_news_socket :) , 30 ); } void remove(){ if(news_socket) news_socket->remove(); log_file("news.data","NEWS OBJECT DESTED\n"); destruct(this_object()); } void check_news_socket() { if(!news_socket){ log_file("news","Lost NEWS connection at "+ctime(time())+".\n"); return; } } void post(){ mode = NEWS_POSTING; send_packet(({ "news-post-req", "TimMUDSUX", 3333, "testify", "thread here", "subject here", "poster", "This is a stupid test." })); }