#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "broadcast.h" #include "command.h" #include "conn_data.h" #include "db.h" #include "event.h" #include "firebase.h" #include "net.h" #include "plugin.h" #include "runloop.h" #include "user_data.h" #include "hashtable.h" #include "config.h" #define PORT (29536) // python: int("msg",36) static int create_server_socket(void){ int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0)die_perror("socket"); int one=1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&one,sizeof one); struct sockaddr_in name; name.sin_family=AF_INET; name.sin_addr.s_addr=htonl(INADDR_ANY); name.sin_port=htons(PORT); if(bind(sock,(struct sockaddr*)&name,sizeof name)<0)die_perror("bind"); if(listen(sock,16)<0)die_perror("listen"); return sock; } static struct hashtable *conn_hash = NULL; static void conn_hash_init(void) { conn_hash = ht_alloc(); } static void add_conn_data(int fd, struct conn_data *item) { ht_insert(conn_hash, fd, item); } static struct conn_data* find_conn_data(int fd){ struct conn_data *item = (struct conn_data*)ht_find(conn_hash, fd); assert(item); return item; } static void delete_conn_data(int fd){ debug("Deleting conn_data for fd=%d",fd); struct conn_data *item = find_conn_data(fd); if (item->userid != -1) { userdata_unregister(item->userid, fd); broadcast_online_change(item->userid); } conn_data_nullify(item); free(item); ht_delete(conn_hash, fd); } static bool client_socket_callback(int fd){ struct conn_data *data=find_conn_data(fd); if(data->bufsz-data->buflen<256){ data->bufsz*=2; data->buffer=realloc(data->buffer,data->bufsz,char); } ssize_t ret; do ret=read(fd,data->buffer+data->buflen,data->bufsz-data->buflen); while(ret<0&&errno==EINTR); if(ret==0||(ret<0&&(errno==ECONNRESET||errno==ETIMEDOUT))){ delete_conn_data(fd); close(fd); return true; } if(ret<0)die_perror("read"); data->buflen+=ret; while(true){ char *lfp=memchr(data->buffer,'\n',data->buflen); if(lfp==NULL)break; size_t length=lfp-data->buffer; bool should_close=handle_input_line(data,data->buffer,length); memmove(data->buffer,lfp+1,data->buflen-length-1); data->buflen-=length+1; if(should_close){ delete_conn_data(fd); close(fd); return true; } } return false; } static bool server_socket_callback(int fd){ int sock; do sock=accept(fd,NULL,NULL); while(sock<0&&errno==EINTR); if(sock<0)die_perror("accept"); runloop_add_fd(sock,client_socket_callback,true); struct conn_data *item = malloc(1, struct conn_data); conn_data_init(item, sock); add_conn_data(sock, item); debug("Added conn_data for fd=%d",sock); return false; } static bool timeout_callback(int fd){ // Note: this does not check the protocol version. If the protocol changes // significantly regarding push messages, perhaps that should change. return net_send_raw_text(fd,"_push ping\n",11); } #ifndef __APPLE__ static void srandomdev(void){ FILE *f=fopen("/dev/urandom","r"); assert(f); unsigned int seed; size_t nread=fread(&seed,1,sizeof(seed),f); assert(nread==sizeof(seed)); fclose(f); srandom(seed); } #endif static void signal_handler(int sig){ if(sig==SIGPIPE){ // ignore } } static bool constructors_work = false; __attribute__((constructor)) static void constructor(void) { constructors_work = true; } int main(int argc,char **argv){ if (!constructors_work) { fprintf(stderr, "This platform does not support application constructors, which tomsg requires\n"); return 1; } srandomdev(); if(sodium_init()<0){ fprintf(stderr,"Could not initialise libsodium!\n"); return 1; } config_init("config.txt"); signal(SIGPIPE,signal_handler); conn_hash_init(); plugin_init(); for(int i=1;i