#define _GNU_SOURCE #include #include #include #include #include #include #include "command.h" #include "db.h" #include "user_data.h" static i64 make_timestamp(void){ struct timeval tv; gettimeofday(&tv,NULL); return (i64)tv.tv_sec+tv.tv_usec; } static bool send_raw_text(int fd,const char *text,i64 len){ i64 cursor=0; while(cursor0){ i64 bufsz=64; buf=malloc(bufsz,char); for(i64 i=0;i=bufsz){ bufsz=len+512; buf=realloc(buf,bufsz,char); } memcpy(buf+1,list[i],len); buf[0]=' '; if(send_raw_text(fd,buf,len+1)){ free(buf); return true; } } free(buf); } return send_raw_text(fd,"\n",1); } static bool cmd_register(struct conn_data *data,const char *tag,const char **args){ i64 userid=db_find_user(args[0]); if(userid!=-1){ send_error(data->fd,tag,"Username already exists"); return false; } db_create_user(args[0],args[1]); return send_ok(data->fd,tag); } static bool cmd_login(struct conn_data *data,const char *tag,const char **args){ i64 userid=db_find_user(args[0]); if(userid==-1){ send_error(data->fd,tag,"User not found"); return false; } char *pass=db_get_pass(userid); bool success=strcmp(args[1],pass)==0; free(pass); if(data->userid!=-1){ userdata_unregister(data->userid,data->fd); } if(success){ data->userid=userid; userdata_register(userid,data->fd); send_ok(data->fd,tag); } else { data->userid=-1; send_error(data->fd,tag,"Incorrect password"); } return false; } static bool cmd_list_rooms(struct conn_data *data,const char *tag,const char **args){ (void)args; if(data->userid==-1){ send_error(data->fd,tag,"Not logged in"); return false; } struct db_room_list rl=db_list_rooms(data->userid); if(rl.count<=0){ db_nullify_room_list(rl); return send_list(data->fd,tag,0,NULL); } const char *names[rl.count]; for(i64 i=0;ifd,tag,rl.count,names); db_nullify_room_list(rl); return closed; } static bool cmd_list_members(struct conn_data *data,const char *tag,const char **args){ if(data->userid==-1){ send_error(data->fd,tag,"Not logged in"); return false; } i64 roomid=db_find_room(args[0]); if(roomid==-1){ send_error(data->fd,tag,"Room not found"); return false; } if(!db_is_member(roomid,data->userid)){ send_error(data->fd,tag,"Not in that room"); return false; } struct db_user_list ul=db_list_members(roomid); if(ul.count<=0){ db_nullify_user_list(ul); return send_list(data->fd,tag,0,NULL); } const char *names[ul.count]; for(i64 i=0;ifd,tag,ul.count,names); db_nullify_user_list(ul); return closed; } static bool cmd_create_room(struct conn_data *data,const char *tag,const char **args){ (void)args; if(data->userid==-1){ send_error(data->fd,tag,"Not logged in"); return false; } struct db_name_id room=db_create_room(); db_add_member(room.id,data->userid); bool closed=send_name(data->fd,tag,room.name); db_nullify_name_id(room); return closed; } static bool cmd_invite(struct conn_data *data,const char *tag,const char **args){ if(data->userid==-1){ send_error(data->fd,tag,"Not logged in"); return false; } i64 roomid=db_find_room(args[0]); if(roomid==-1){ send_error(data->fd,tag,"Room not found"); return false; } i64 user2=db_find_user(args[1]); if(user2==-1){ send_error(data->fd,tag,"User not found"); return false; } if(!db_is_member(roomid,data->userid)){ send_error(data->fd,tag,"Not in that room"); return false; } db_add_member(roomid,user2); return send_ok(data->fd,tag); } static bool cmd_send(struct conn_data *data,const char *tag,const char **args){ if(data->userid==-1){ send_error(data->fd,tag,"Not logged in"); return false; } i64 roomid=db_find_room(args[0]); if(roomid==-1){ send_error(data->fd,tag,"Room not found"); return false; } if(!db_is_member(roomid,data->userid)){ send_error(data->fd,tag,"Not in that room"); return false; } db_create_message(roomid,data->userid,make_timestamp(),args[1]); bool closed=send_ok(data->fd,tag); char *pushbuf=NULL; char *username=db_get_username(data->userid); i64 pushbuflen=asprintf(&pushbuf,"_push message %s %s %s\n",args[0],username,args[1]); assert(pushbuf); free(username); struct db_user_list members=db_list_members(roomid); for(i64 i=0;ifd){ send_raw_text(fds[j],pushbuf,pushbuflen); } } } free(pushbuf); return closed; } struct cmd_info{ const char *cmdname; int nargs; bool longlast; // whether the last argument should span the rest of the input line bool (*handler)(struct conn_data *data,const char *tag,const char **args); }; static const struct cmd_info commands[]={ {"register",2,true,cmd_register}, {"login",2,true,cmd_login}, {"list_rooms",0,false,cmd_list_rooms}, {"list_members",1,false,cmd_list_members}, {"create_room",0,false,cmd_create_room}, {"invite",2,false,cmd_invite}, {"send",2,true,cmd_send}, }; #define NCOMMANDS (sizeof(commands)/sizeof(commands[0])) bool handle_input_line(struct conn_data *data,char *line,size_t linelen){ line[linelen]='\0'; char *sepp=memchr(line,' ',linelen); if(sepp==NULL){ debug("No space in input line from connection %d",data->fd); return true; } char *tag=line; size_t taglen=sepp-tag; *sepp='\0'; line+=taglen+1; linelen-=taglen+1; sepp=memchr(line,' ',linelen); if(sepp==NULL)sepp=line+linelen; size_t cmdlen=sepp-line; size_t cmdi; for(cmdi=0;cmdifd); return true; } int nargs=commands[cmdi].nargs; char *args[nargs]; size_t cursor=cmdlen+1; for(int i=0;ilinelen){ debug("Connection %d sent too few parameters to command %s",data->fd,commands[cmdi].cmdname); return true; } if(i==nargs-1&&commands[cmdi].longlast){ sepp=line+linelen; } else { sepp=memchr(line+cursor,' ',linelen-cursor); if(sepp==NULL)sepp=line+linelen; } *sepp='\0'; args[i]=line+cursor; cursor=sepp-line+1; } if(sepp-line<(i64)linelen){ debug("Connection %d sent too many parameters to command %s",data->fd,commands[cmdi].cmdname); return true; } return commands[cmdi].handler(data,tag,(const char**)args); }