/************************************************************************** Close the dialogs for all cities. **************************************************************************/ void popdown_all_city_dialogs(void) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); }
/************************************************************************** Saves pconn fields to the database. If the username already exists, replace the data. **************************************************************************/ static bool auth_db_save(struct connection *pconn) { #ifdef HAVE_AUTH char buffer[1024] = ""; const int bufsize = sizeof(buffer); char *name_buffer = NULL; char *pw_buffer = NULL; MYSQL *sock, mysql; int str_result; mysql_init(&mysql); /* attempt to connect to the server */ if (!(sock = mysql_real_connect(&mysql, auth_config.host.value, auth_config.user.value, auth_config.password.value, auth_config.database.value, atoi(auth_config.port.value), NULL, 0))) { freelog(LOG_ERROR, "Can't connect to server! (%s)", mysql_error(&mysql)); return FALSE; } name_buffer = alloc_escaped_string(&mysql, pconn->username); pw_buffer = alloc_escaped_string(&mysql, pconn->server.password); if (name_buffer == NULL || pw_buffer == NULL) { free_escaped_string(name_buffer); mysql_close(sock); return FALSE; } /* insert new user into table. we insert the following things: name * md5sum of the password, the creation time in seconds, the accesstime * also in seconds from 1970, the users address (twice) and the logincount */ str_result = my_snprintf(buffer, bufsize, "insert into %s values " "(NULL, '%s', '%s', NULL, " "unix_timestamp(), unix_timestamp()," "'%s', '%s', 0)", auth_config.table.value, name_buffer, pw_buffer, pconn->server.ipaddr, pconn->server.ipaddr); /* Password is not needed for further queries. */ free_escaped_string(pw_buffer); pw_buffer = NULL; if (str_result < 0 || str_result >= bufsize || mysql_query(sock, buffer)) { freelog(LOG_ERROR, "db_save insert failed for new user: %s (%s)", pconn->username, mysql_error(sock)); mysql_close(sock); return FALSE; } /* insert an entry into our log */ memset(buffer, 0, bufsize); str_result = my_snprintf(buffer, bufsize, "insert into %s (name, logintime, address, succeed) " "values ('%s',unix_timestamp(),'%s', 'S')", auth_config.login_table.value, name_buffer, pconn->server.ipaddr); free_escaped_string(name_buffer); name_buffer = 0; if (str_result < 0 || str_result >= bufsize || mysql_query(sock, buffer)) { freelog(LOG_ERROR, "db_load insert loginlog failed for user: %s (%s)", pconn->username, mysql_error(sock)); } mysql_close(sock); #endif return TRUE; }
/************************************************************************** Close the dialog for the given city. **************************************************************************/ void popdown_city_dialog(struct city *pcity) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); }
/************************************************************************** handle authentication of a user; called by handle_login_request() if authentication is enabled. if the connection is rejected right away, return FALSE, otherwise return TRUE **************************************************************************/ bool authenticate_user(struct connection *pconn, char *username) { char tmpname[MAX_LEN_NAME] = "\0"; /* assign the client a unique guest name/reject if guests aren't allowed */ if (is_guest_name(username)) { if (srvarg.auth_allow_guests) { sz_strlcpy(tmpname, username); get_unique_guest_name(username); if (strncmp(tmpname, username, MAX_LEN_NAME) != 0) { notify_conn(pconn->self, NULL, E_CONNECTION, ftc_warning, _("Warning: the guest name '%s' has been " "taken, renaming to user '%s'."), tmpname, username); } sz_strlcpy(pconn->username, username); establish_new_connection(pconn); } else { reject_new_connection(_("Guests are not allowed on this server. " "Sorry."), pconn); freelog(LOG_NORMAL, _("%s was rejected: Guests not allowed."), username); return FALSE; } } else { /* we are not a guest, we need an extra check as to whether a * connection can be established: the client must authenticate itself */ char buffer[MAX_LEN_MSG]; sz_strlcpy(pconn->username, username); switch(auth_db_load(pconn)) { case AUTH_DB_ERROR: if (srvarg.auth_allow_guests) { sz_strlcpy(tmpname, pconn->username); get_unique_guest_name(tmpname); /* don't pass pconn->username here */ sz_strlcpy(pconn->username, tmpname); freelog(LOG_ERROR, "Error reading database; connection -> guest"); notify_conn(pconn->self, NULL, E_CONNECTION, ftc_warning, _("There was an error reading the user " "database, logging in as guest connection '%s'."), pconn->username); establish_new_connection(pconn); } else { reject_new_connection(_("There was an error reading the user database " "and guest logins are not allowed. Sorry"), pconn); freelog(LOG_NORMAL, _("%s was rejected: Database error and guests not allowed."), pconn->username); return FALSE; } break; case AUTH_DB_SUCCESS: /* we found a user */ my_snprintf(buffer, sizeof(buffer), _("Enter password for %s:"), pconn->username); dsend_packet_authentication_req(pconn, AUTH_LOGIN_FIRST, buffer); pconn->server.auth_settime = time(NULL); pconn->server.status = AS_REQUESTING_OLD_PASS; break; case AUTH_DB_NOT_FOUND: /* we couldn't find the user, he is new */ if (srvarg.auth_allow_newusers) { sz_strlcpy(buffer, _("Enter a new password (and remember it).")); dsend_packet_authentication_req(pconn, AUTH_NEWUSER_FIRST, buffer); pconn->server.auth_settime = time(NULL); pconn->server.status = AS_REQUESTING_NEW_PASS; } else { reject_new_connection(_("This server allows only preregistered " "users. Sorry."), pconn); freelog(LOG_NORMAL, _("%s was rejected: Only preregistered users allowed."), pconn->username); return FALSE; } break; default: assert(0); break; } return TRUE; } return TRUE; }
/************************************************************************** Loads a user from the database. **************************************************************************/ static enum authdb_status auth_db_load(struct connection *pconn) { #ifdef HAVE_AUTH char buffer[512] = ""; const int bufsize = sizeof(buffer); int num_rows = 0; MYSQL *sock, mysql; MYSQL_RES *res; MYSQL_ROW row; char *name_buffer; int str_result; mysql_init(&mysql); /* attempt to connect to the server */ if (!(sock = mysql_real_connect(&mysql, auth_config.host.value, auth_config.user.value, auth_config.password.value, auth_config.database.value, atoi(auth_config.port.value), NULL, 0))) { freelog(LOG_ERROR, "Can't connect to server! (%s)", mysql_error(&mysql)); return AUTH_DB_ERROR; } name_buffer = alloc_escaped_string(&mysql, pconn->username); if (name_buffer != NULL) { /* select the password from the entry */ str_result = my_snprintf(buffer, bufsize, "select password from %s where name = '%s'", auth_config.table.value, name_buffer); if (str_result < 0 || str_result >= bufsize || mysql_query(sock, buffer)) { freelog(LOG_ERROR, "db_load query failed for user: %s (%s)", pconn->username, mysql_error(sock)); free_escaped_string(name_buffer); mysql_close(sock); return AUTH_DB_ERROR; } res = mysql_store_result(sock); num_rows = mysql_num_rows(res); /* if num_rows = 0, then we could find no such user */ if (num_rows < 1) { mysql_free_result(res); free_escaped_string(name_buffer); mysql_close(sock); return AUTH_DB_NOT_FOUND; } /* if there are more than one row that matches this name, it's an error * continue anyway though */ if (num_rows > 1) { freelog(LOG_ERROR, "db_load query found multiple entries (%d) for user: %s", num_rows, pconn->username); } /* if there are rows, then fetch them and use the first one */ row = mysql_fetch_row(res); mystrlcpy(pconn->server.password, row[0], sizeof(pconn->server.password)); mysql_free_result(res); /* update the access time for this user */ memset(buffer, 0, bufsize); str_result = my_snprintf(buffer, bufsize, "update %s set accesstime=unix_timestamp(), " "address='%s', logincount=logincount+1 " "where strcmp(name, '%s') = 0", auth_config.table.value, pconn->server.ipaddr, name_buffer); free_escaped_string(name_buffer); name_buffer = NULL; if (str_result < 0 || str_result >= bufsize || mysql_query(sock, buffer)) { freelog(LOG_ERROR, "db_load update accesstime failed for user: %s (%s)", pconn->username, mysql_error(sock)); } } mysql_close(sock); #endif return AUTH_DB_SUCCESS; }
/************************************************************************** Returns current client page **************************************************************************/ enum client_pages get_client_page(void) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); /* PORTME */ return PAGE_MAIN; }
/************************************************************************** update the start page. **************************************************************************/ void update_start_page(void) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); /* PORTME */ }
/************************************************************************** Sets the "page" that the client should show. See also pages_g.h. **************************************************************************/ void set_client_page(enum client_pages page) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); /* PORTME */ }
/**************************************************************************** Set the list of available rulesets. The default ruleset should be "default", and if the user changes this then set_ruleset() should be called. ****************************************************************************/ void gui_set_rulesets(int num_rulesets, char **rulesets) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); /* PORTME */ }
/************************************************************************** A wrapper around read_socket_data() which also handles the case the socket becomes writeable and there is still data which should be sent to the server. Returns: -1 : an error occurred - you should close the socket >0 : number of bytes read =0 : no data read, would block **************************************************************************/ static int read_from_connection(struct connection *pc, bool block) { for (;;) { fd_set readfs, writefs, exceptfs; int socket_fd = pc->sock; bool have_data_for_server = (pc->used && pc->send_buffer && pc->send_buffer->ndata > 0); int n; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; MY_FD_ZERO(&readfs); FD_SET(socket_fd, &readfs); MY_FD_ZERO(&exceptfs); FD_SET(socket_fd, &exceptfs); if (have_data_for_server) { MY_FD_ZERO(&writefs); FD_SET(socket_fd, &writefs); n = fc_select(socket_fd + 1, &readfs, &writefs, &exceptfs, block ? NULL : &tv); } else { n = fc_select(socket_fd + 1, &readfs, NULL, &exceptfs, block ? NULL : &tv); } /* the socket is neither readable, writeable nor got an exception */ if (n == 0) { return 0; } if (n == -1) { if (errno == EINTR) { /* EINTR can happen sometimes, especially when compiling with -pg. * Generally we just want to run select again. */ freelog(LOG_DEBUG, "select() returned EINTR"); continue; } freelog(LOG_ERROR, "select() return=%d errno=%d (%s)", n, errno, fc_strerror(fc_get_errno())); return -1; } if (FD_ISSET(socket_fd, &exceptfs)) { return -1; } if (have_data_for_server && FD_ISSET(socket_fd, &writefs)) { flush_connection_send_buffer_all(pc); } if (FD_ISSET(socket_fd, &readfs)) { return read_socket_data(socket_fd, pc->buffer); } } }
/*************************************************************************** Look up the service at hostname:port and fill in *sa. ***************************************************************************/ bool net_lookup_service(const char *name, int port, union fc_sockaddr *addr, bool force_ipv4) { struct hostent *hp; struct sockaddr_in *sock4; #ifdef IPV6_SUPPORT struct sockaddr_in6 *sock6; #endif /* IPv6 support */ sock4 = &addr->saddr_in4; #ifdef IPV6_SUPPORT sock6 = &addr->saddr_in6; if (!force_ipv4) { addr->saddr.sa_family = AF_INET6; sock6->sin6_port = htons(port); if (!name) { sock6->sin6_addr = in6addr_any; return TRUE; } if (inet_pton(AF_INET6, name, &sock6->sin6_addr)) { return TRUE; } /* TODO: Replace gethostbyname2() with getaddrinfo() */ hp = gethostbyname2(name, AF_INET6); } else #endif /* IPv6 support */ { addr->saddr.sa_family = AF_INET; sock4->sin_port = htons(port); if (!name) { sock4->sin_addr.s_addr = htonl(INADDR_ANY); return TRUE; } } #ifdef IPV6_SUPPORT if (force_ipv4 || !hp || hp->h_addrtype != AF_INET6) { /* Try to fallback to IPv4 resolution */ if (!force_ipv4) { freelog(LOG_DEBUG, "Falling back to IPv4"); } hp = gethostbyname2(name, AF_INET); if (!hp || hp->h_addrtype != AF_INET) { return FALSE; } addr->saddr.sa_family = AF_INET; sock4->sin_port = htons(port); } #else /* IPV6 support */ #if defined(HAVE_INET_ATON) if (inet_aton(name, &sock4->sin_addr) != 0) { return TRUE; } #else /* HAVE_INET_ATON */ if ((sock4->sin_addr.s_addr = inet_addr(name)) != INADDR_NONE) { return TRUE; } #endif /* HAVE_INET_ATON */ hp = gethostbyname(name); if (!hp || hp->h_addrtype != AF_INET) { return FALSE; } #endif /* IPv6 support */ #ifdef IPV6_SUPPORT if (addr->saddr.sa_family == AF_INET6) { memcpy(&sock6->sin6_addr, hp->h_addr, hp->h_length); } else #endif /* IPv6 support */ { memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length); } return TRUE; }
/************************************************************************** Update all information in the player list dialog. **************************************************************************/ void update_players_dialog(void) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); /* PORTME */ }
/************************************************************************** Display the player list dialog. Optionally raise it. **************************************************************************/ void popup_players_dialog(bool raise) { freelog(LOG_VERBOSE, "Port Me %s [@%s:%d]", __func__, __FILE__, __LINE__); /* PORTME */ }
/********************************************************************** Read a new line into cur_line; also copy to copy_line. Increments line_num and cur_line_pos. Returns 0 if didn't read or other problem: treat as EOF. Strips newline from input. ***********************************************************************/ static bool read_a_line(struct inputfile *inf) { struct astring *line; char *ret; int pos; assert_sanity(inf); if (inf->at_eof) return FALSE; /* abbreviation: */ line = &inf->cur_line; /* minimum initial line length: */ astr_minsize(line, 80); pos = 0; /* don't print "orig line" in warnings until we have it: */ inf->copy_line.n = 0; /* Read until we get a full line: * At start of this loop, pos is index to trailing null * (or first position) in line. */ for(;;) { ret = fz_fgets(line->str + pos, line->n_alloc - pos, inf->fp); if (!ret) { /* fgets failed */ inf->at_eof = TRUE; if (inf->in_string) { /* Note: Don't allow multi-line strings to cross "include" * boundaries */ inf_log(inf, LOG_ERROR, "Multi-line string went to end-of-file"); return FALSE; } break; } pos += strlen(line->str + pos); line->n = pos + 1; if (line->str[pos-1] == '\n') { line->str[pos-1] = '\0'; line->n--; break; } if (line->n != line->n_alloc) { freelog(LOG_VERBOSE, "inputfile: expect missing newline at EOF"); } astr_minsize(line, line->n*2); } inf->line_num++; inf->cur_line_pos = 0; astr_minsize(&inf->copy_line, inf->cur_line.n + ((inf->cur_line.n == 0) ? 1 : 0)); strcpy(inf->copy_line.str, inf->cur_line.str); if (check_include(inf)) { return read_a_line(inf); } if (inf->at_eof) { line->str[0] = '\0'; line->n = 0; if (inf->included_from) { /* Pop the include, and get next line from file above instead. */ struct inputfile *inc = inf->included_from; inf_close_partial(inf); *inf = *inc; /* so the user pointer in still valid * (and inf pointers in calling functions) */ free(inc); return read_a_line(inf); } return FALSE; } else { return TRUE; } }
/********************************************************************** Check for an include command, which is an isolated line with: *include "filename" If a file is included via this mechanism, returns 1, and sets up data appropriately: (*inf) will now correspond to the new file, which is opened but no data read, and inf->included_from is set to newly malloced memory which corresponds to the old file. ***********************************************************************/ static bool check_include(struct inputfile *inf) { const char *include_prefix = "*include"; static size_t len = 0; char *bare_name, *full_name, *c; struct inputfile *new_inf, temp; if (len==0) { len = strlen(include_prefix); } assert_sanity(inf); if (inf->in_string || inf->cur_line.n <= len || inf->cur_line_pos > 0) { return FALSE; } if (strncmp(inf->cur_line.str, include_prefix, len)!=0) { return FALSE; } /* from here, the include-line must be well formed or we die */ /* keep inf->cur_line_pos accurate just so error messages are useful */ /* skip any whitespace: */ inf->cur_line_pos = len; c = inf->cur_line.str + len; while (*c != '\0' && my_isspace(*c)) c++; if (*c != '\"') { inf_log(inf, LOG_ERROR, "Did not find opening doublequote for '*include' line"); return FALSE; } c++; inf->cur_line_pos = c - inf->cur_line.str; bare_name = c; while (*c != '\0' && *c != '\"') c++; if (*c != '\"') { inf_log(inf, LOG_ERROR, "Did not find closing doublequote for '*include' line"); return FALSE; } *c++ = '\0'; inf->cur_line_pos = c - inf->cur_line.str; /* check rest of line is well-formed: */ while (*c != '\0' && my_isspace(*c) && !is_comment(*c)) c++; if (!(*c=='\0' || is_comment(*c))) { inf_log(inf, LOG_ERROR, "Junk after filename for '*include' line"); return FALSE; } inf->cur_line_pos = inf->cur_line.n-1; full_name = inf->datafn(bare_name); if (!full_name) { freelog(LOG_ERROR, "Could not find included file \"%s\"", bare_name); return FALSE; } /* avoid recursion: (first filename may not have the same path, but will at least stop infinite recursion) */ { struct inputfile *inc = inf; do { if (inc->filename && strcmp(full_name, inc->filename)==0) { freelog(LOG_ERROR, "Recursion trap on '*include' for \"%s\"", full_name); return FALSE; } } while((inc=inc->included_from)); } new_inf = inf_from_file(full_name, inf->datafn); /* Swap things around so that memory pointed to by inf (user pointer, and pointer in calling functions) contains the new inputfile, and newly allocated memory for new_inf contains the old inputfile. This is pretty scary, lets hope it works... */ temp = *new_inf; *new_inf = *inf; *inf = temp; inf->included_from = new_inf; return TRUE; }
/************************************************************************** Convert string from display encoding (16 bit unicode) to local encoding (8 bit char) and resut put in 'pToString'. if 'pToString' == NULL then resulting string will be allocate automaticaly. 'length' give real sizeof 'pToString' array. Function return (char *) pointer to (new) pToString. **************************************************************************/ char *convertcopy_to_chars(char *pToString, size_t length, const Uint16 * pFromUniString) { /* Start Parametrs */ const char *pFromcode = get_display_encoding(); const char *pTocode = get_internal_encoding(); const char *pStart = (char *) pFromUniString; size_t ulength = (unistrlen(pFromUniString) + 1) * 2; /* ===== */ char *pResult; iconv_t cd; /* ===== */ if (!pStart) { return pToString; } cd = iconv_open(pTocode, pFromcode); if (cd == (iconv_t) (-1)) { if (errno != EINVAL) { return pToString; } } if(pToString) { pResult = pToString; } else { length = ulength * 2; /* UTF-8: up to 4 bytes per char */ pResult = fc_calloc(1, length); } iconv(cd, NULL, NULL, NULL, NULL); /* return to the initial state */ /* Do the conversion for real. */ { const char *pInptr = pStart; size_t Insize = ulength; char *pOutptr = pResult; size_t Outsize = length; while (Insize > 0 && Outsize > 0) { size_t Res = iconv(cd, (ICONV_CONST char **) &pInptr, &Insize, &pOutptr, &Outsize); if (Res == (size_t) (-1)) { freelog(LOG_ERROR, "iconv() error: %s", fc_strerror(fc_get_errno())); if (errno == EINVAL) { break; } else { int saved_errno = errno; iconv_close(cd); errno = saved_errno; if(!pToString) { FC_FREE(pResult); } return pToString; } } } { size_t Res = iconv(cd, NULL, NULL, &pOutptr, &Outsize); if (Res == (size_t) (-1)) { int saved_errno = errno; iconv_close(cd); errno = saved_errno; if(!pToString) { FC_FREE(pResult); } return pToString; } } } iconv_close(cd); return pResult; }