#define _GNU_SOURCE #include #include #include #include #include #include #include #include "net.h" extern FILE *debugf; struct store_item{ char *id; net_callback_t *cb; void *payload; }; static i64 store_cap=16,store_len=0; static struct store_item *store=NULL; static net_callback_t *push_callback=NULL; static net_callback_t *history_callback=NULL; #define ALPHABET "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" static void uniqid8(char *dst){ static i64 static_id=0; static const int alphabet_len=strlen(ALPHABET); int id=static_id; static_id++; for(int i=7;i>=0;i--){ dst[i]=ALPHABET[id%alphabet_len]; id/=alphabet_len; } } void net_set_push_callback(net_callback_t *callback){ push_callback=callback; } void net_set_history_callback(net_callback_t *callback){ history_callback=callback; } __attribute__((format (printf, 4, 5))) bool net_sendf(int fd,net_callback_t *callback,void *payload,const char *format,...){ if(store==NULL){ store=malloc(store_cap*sizeof(struct store_item)); } else if(store_len==store_cap){ store_cap*=2; store=realloc(store,store_cap*sizeof(struct store_item)); } assert(store); fprintf(debugf,"net_sendf(%d,%p,\"%s\",...)\n",fd,callback,format); va_list ap,ap2; va_start(ap,format); va_copy(ap2,ap); i64 len=vsnprintf(NULL,0,format,ap2); va_end(ap2); assert(len>=0); char *buf=malloc(9+len+2); assert(buf); uniqid8(buf); buf[8]=' '; i64 len2=vsprintf(buf+9,format,ap); va_end(ap); assert(len2==len); buf[9+len]='\n'; buf[9+len+1]='\0'; store[store_len].id=malloc(9); assert(store[store_len].id); memcpy(store[store_len].id,buf,8); store[store_len].id[8]='\0'; store[store_len].cb=callback; store[store_len].payload=payload; store_len++; i64 cursor=0,total=9+len+1; while(cursor\n",msg); return; } i64 taglen=p-msg; net_callback_t *cb=NULL; void *payload=NULL; if(taglen==5&&memcmp(msg,"_push",5)==0){ cb=push_callback; } else if(*p!='\0'&&memcmp(p+1,"history_message",15)==0){ cb=history_callback; } else { if(taglen!=8){ fprintf(debugf,"net_handle_recv: tag not length 8 <%s>\n",msg); return; } for(i64 i=0;i\n",msg); return; } } const char *cmd=p+1; p=strchr(cmd,' '); if(p==NULL)p=msg+msglen; i64 cmdlen=p-cmd; // Now `p` points to the space (or '\0') after the command name if(cmdlen==2&&memcmp(cmd,"ok",2)==0){ cb(fd,(struct net_response){.type=NET_OK},payload); } else if(cmdlen==6&&memcmp(cmd,"number",6)==0){ if(*p=='\0'){ fprintf(debugf,"net_handle_recv: no number argument <%s>\n",msg); return; } const char *nump=p+1; struct net_response res=(struct net_response){ .type=NET_NUMBER }; const char *endp; res.number=strtol(nump,(char**)&endp,10); if(nump[0]=='\0'||*endp!='\0'){ fprintf(debugf,"net_handle_recv: invalid number argument <%s>\n",msg); return; } cb(fd,res,payload); } else if(cmdlen==5&&memcmp(cmd,"error",5)==0){ struct net_response res=(struct net_response){ .type=NET_ERROR, .error=strdup(*p=='\0'?p:p+1) }; cb(fd,res,payload); free(res.error); } else if(cmdlen==4&&memcmp(cmd,"name",4)==0){ struct net_response res=(struct net_response){ .type=NET_NAME, .name=strdup(*p=='\0'?p:p+1) }; cb(fd,res,payload); free(res.name); } else if(cmdlen==4&&memcmp(cmd,"ping",4)==0){ // Do nothing } else if(cmdlen==4&&memcmp(cmd,"pong",4)==0){ struct net_response res=(struct net_response){ .type=NET_PONG }; cb(fd,res,payload); } else if(cmdlen==4&&memcmp(cmd,"list",4)==0){ struct net_response res=(struct net_response){.type=NET_LIST}; if(*p=='\0'){ fprintf(debugf,"net_handle_recv: no list count <%s>\n",msg); return; } const char *cursor=p+1; p=strchr(cursor,' '); res.nitems=strtol(cursor,NULL,10); if(res.nitems<=0){ fprintf(debugf,"net_handle_recv: -- 0 items <%s>\n",msg); res.nitems=0; res.items=NULL; cb(fd,res,payload); return; } res.items=malloc(res.nitems*sizeof(char*)); assert(res.items); cursor=p; for(i64 i=0;i\n",msg); return; } cursor++; p=strchr(cursor,' '); if(p==NULL)p=msg+msglen; res.items[i]=malloc(p-cursor+1); assert(res.items[i]); memcpy(res.items[i],cursor,p-cursor); res.items[i][p-cursor]='\0'; fprintf(debugf,"net_handle_recv: -- item \"%s\" <%s>\n",res.items[i],msg); cursor=p; } cb(fd,res,payload); for(i64 i=0;i\n",msg); return; } const char *roomp=p+1; p=strchr(roomp,' '); const char *q,*r; if(p==NULL||(q=strchr(p+1,' '))==NULL||(r=strchr(q+1,' '))==NULL){ fprintf(debugf,"net_handle_recv: not enough arguments to 'message' <%s>\n",msg); return; } i64 roomlen=p-roomp; const char *usernamep=p+1; i64 usernamelen=q-usernamep; const char *stampp=q+1; i64 stamplen=r-stampp; const char *textp=r+1; i64 textlen=msglen-(textp-msg); struct net_response res; res.type=NET_MESSAGE; const char *endp; res.timestamp=strtoll(stampp,(char**)&endp,10); if(endp-stampp!=stamplen){ fprintf(debugf,"net_handle_recv: timestamp not a number in 'message' <%s>\n",msg); return; } res.room=malloc(roomlen+1); res.username=malloc(usernamelen+1); res.message=malloc(textlen+1); assert(res.room&&res.username&&res.message); memcpy(res.room,roomp,roomlen); res.room[roomlen]='\0'; memcpy(res.username,usernamep,usernamelen); res.username[usernamelen]='\0'; memcpy(res.message,textp,textlen); res.message[textlen]='\0'; cb(fd,res,payload); free(res.room); free(res.username); free(res.message); } else if(cmdlen==7&&memcmp(cmd,"history",7)==0){ struct net_response res; res.type=NET_OK; cb(fd,res,payload); } else if(cmdlen==15&&memcmp(cmd,"history_message",15)==0){ if(*p=='\0'){ fprintf(debugf,"net_handle_recv: no arguments to 'history_message' <%s>\n",msg); return; } p=strchr(p+1,' '); if(*p=='\0'){ fprintf(debugf,"net_handle_recv: no arguments past index to 'history_message' <%s>\n",msg); return; } const char *roomp=p+1; p=strchr(roomp,' '); const char *q,*r; if(p==NULL||(q=strchr(p+1,' '))==NULL||(r=strchr(q+1,' '))==NULL){ fprintf(debugf,"net_handle_recv: not enough arguments to 'history_message' <%s>\n",msg); return; } i64 roomlen=p-roomp; const char *usernamep=p+1; i64 usernamelen=q-usernamep; const char *stampp=q+1; i64 stamplen=r-stampp; const char *textp=r+1; i64 textlen=msglen-(textp-msg); struct net_response res; res.type=NET_HISTORY; const char *endp; res.timestamp=strtoll(stampp,(char**)&endp,10); if(endp-stampp!=stamplen){ fprintf(debugf,"net_handle_recv: timestamp not a number in 'history_message' <%s>\n",msg); return; } res.room=malloc(roomlen+1); res.username=malloc(usernamelen+1); res.message=malloc(textlen+1); assert(res.room&&res.username&&res.message); memcpy(res.room,roomp,roomlen); res.room[roomlen]='\0'; memcpy(res.username,usernamep,usernamelen); res.username[usernamelen]='\0'; memcpy(res.message,textp,textlen); res.message[textlen]='\0'; cb(fd,res,payload); free(res.room); free(res.username); free(res.message); } else if(cmdlen==4&&memcmp(cmd,"join",4)==0){ const char *q; if(*p=='\0'||(q=strchr(p+1,' '))==NULL){ fprintf(debugf,"net_handle_recv: not enough arguments to 'join' <%s>\n",msg); return; } const char *roomp=p+1; i64 roomlen=q-roomp; const char *userp=q+1; i64 userlen=msglen-(userp-msg); struct net_response res; res.type=NET_JOIN; res.room=malloc(roomlen+1); res.username=malloc(userlen+1); assert(res.room&&res.username); memcpy(res.room,roomp,roomlen); res.room[roomlen]='\0'; memcpy(res.username,userp,userlen); res.username[userlen]='\0'; cb(fd,res,payload); free(res.room); free(res.username); } else if(cmdlen==6&&memcmp(cmd,"invite",6)==0){ if(*p=='\0'){ fprintf(debugf,"net_handle_recv: no arguments to 'invite' <%s>\n",msg); return; } const char *roomp=p+1; i64 roomlen=msglen-(roomp-msg); struct net_response res; res.type=NET_INVITE; res.room=malloc(roomlen+1); assert(res.room); memcpy(res.room,roomp,roomlen); res.room[roomlen]='\0'; cb(fd,res,payload); free(res.room); } else if(cmdlen==6&&memcmp(cmd,"online",6)==0){ const char *q; if(*p=='\0'||(q=strchr(p+1,' '))==NULL){ fprintf(debugf,"net_handle_recv: not enough arguments to 'online' <%s>\n",msg); return; } const char *nump=p+1; const char *usernamep=q+1; const char *endp; struct net_response res=(struct net_response){ .type=NET_ONLINE, .online.username=strdup(usernamep), .online.num=strtol(nump,(char**)&endp,10) }; if(nump[0]==' '||*endp!=' '){ fprintf(debugf,"net_handle_recv: invalid number argument to 'online' <%s>\n",msg); return; } cb(fd,res,payload); free(res.online.username); } else { fprintf(debugf,"net_handle_recv: unknown command <%s>\n",msg); } }