Changeset - c318109520cc
[Not reviewed]
default
0 18 0
Nathan Brink (binki) - 15 years ago 2010-08-07 20:08:12
ohnobinki@ohnopublishing.net
User authentication and some access checking.
18 files changed with 623 insertions and 299 deletions:
0 comments (0 inline, 0 general)
doc/architecture.txt
Show inline comments
 
@@ -55,12 +55,16 @@ Concepts:
 
- client: A distren client is able to submit, query state of, and download completed frames of
 
      jobs registered in a server.
 
  - servers: A server, though having many more functions, shall be able to also perform the list
 
      of actions a client may perform.
 

	
 
- user: A user is an entity which is given access to a distren server.
 
  - user identifiction: As users are per-server, a user shall be identified by the name of the
 
      server he has an account on combined with his handle. A tilde prefixes the username to
 
      differentiate this URL from a job's URL.
 
      distren://<servername>/~<username> , like distren://ohnopub.net/~ohnobinki
 

	
 
- file: There are different uses of files above and distributed rendering requires file distribution.
 
  - file identification: Every file mentioned above was at least in the context of a job. Thus,
 
      file identification numbers shall be assigned in the context of a job identification number.
 
      They shall, however, be numeric.
 
      distren://<servername>/<jobid>/file/<fileid>
etc/distrencommon.conf
Show inline comments
 
@@ -25,9 +25,10 @@ server ohnopublishing
 

	
 

	
 
server localhost
 
{
 
  hostname = "localhost"
 
  username = "test"
 
  password = "xyzzy123"
 
  method = "tcp"
 
  types = {"submit", "distribution", "master"}
 
}
src/client/libdistren.c
Show inline comments
 
@@ -44,12 +44,15 @@ int distren_init(distren_t *handle)
 
{
 
  int tmp;
 

	
 
  struct distren_request *req;
 
  void *data;
 

	
 
  const char *username;
 
  const char *pass;
 

	
 
  if(!handle)
 
    return 1;
 

	
 
  *handle = malloc(sizeof(struct distren));
 
  if(!*handle)
 
    return 1;
 
@@ -61,12 +64,26 @@ int distren_init(distren_t *handle)
 
    {
 
      fprintf(stderr, "error getting configuration\n");
 
      distren_init_cleanup(*handle);
 
      return 1;
 
    }
 

	
 
  tmp = remoteio_authinfo_get((*handle)->options->remoteio,
 
			      (*handle)->server,
 
			      &username,
 
			      &pass);
 
  if(tmp
 
     || !username
 
     || !pass)
 
    {
 
      fprintf(stderr, "error: unable to find information necessary to connect to the server named ``%s'', please check your configuration file.\n",
 
	      (*handle)->server);
 
      distren_init_cleanup(*handle);
 
      return 1;
 
    }
 

	
 
  tmp = remoteio_open_server(&(*handle)->rem,
 
			     (*handle)->options->remoteio,
 
			     (remoteio_read_handle_func_t)&libdistren_remoteio_read_handle,
 
			     *handle,
 
			     (remoteio_close_handle_func_t)&libdistren_remoteio_close_handle,
 
			     (*handle)->server);
 
@@ -87,15 +104,46 @@ int distren_init(distren_t *handle)
 
      distren_init_cleanup(*handle);
 
      return 1;
 
    }
 
  distren_request_send((*handle)->rem, req, data);
 
  distren_request_free_with_data(req, data);
 

	
 
  tmp = distren_request_pass(&req, &data, username, pass);
 
  if(tmp)
 
    {
 
      fprintf(stderr, "error: unable to allocate request");
 

	
 
      distren_init_cleanup(*handle);
 
      return 1;
 
    }
 
  distren_request_send((*handle)->rem, req, data);
 
  distren_request_free_with_data(req, data);
 

	
 
  /*
 
   * There is no response to the DISTREN_REQUEST_PASS packet. However,
 
   * we want to ensure that the username/password we sent are valid
 
   * before returning to the claler. Thus, we here send a PING
 
   * packet. If we get the DISTREN_REQUEST_PONG, we know we're
 
   * authenticated. Otherwise, we'll receive a quit/discconect packet.
 
   */
 
  tmp = distren_request_poing(&req, &data, 1, "auth test", strlen("auth test"));
 
  if(tmp)
 
    {
 
      fprintf(stderr, "error: Unable to allocate a DISTREN_REQUEST_PING\n");
 

	
 
      distren_init_cleanup(*handle);
 
      return 1;
 
    }
 
  distren_request_send((*handle)->rem, req, data);
 
  distren_request_free_with_data(req, data);
 

	
 
  /* flush out the above packets. */
 
  while((*handle)->rem
 
	&& (*handle)->state == DISTREN_STATE_VERSION)
 
      multiio_poll((*handle)->multiio, 500);
 
	&& ((*handle)->state == DISTREN_STATE_VERSION
 
	    || (*handle)->state == DISTREN_STATE_AUTH))
 
    multiio_poll((*handle)->multiio, 500);
 

	
 
  if(!(*handle)->rem)
 
    {
 
      distren_init_cleanup(*handle);
 
      return 1;
 
    }
src/client/libdistren.h
Show inline comments
 
@@ -40,13 +40,12 @@ enum distren_state
 
    DISTREN_STATE_VERSION,
 
    /**
 
     * We are waiting to be authenticated.
 
     */
 
    DISTREN_STATE_AUTH,
 
    DISTREN_STATE_NORMAL,
 
    DISTREN_STATE_UPLOADING,
 
  };
 

	
 
struct distren
 
{
 
  struct options_common *options;
 
  char *server;
src/client/libdistren_request.c
Show inline comments
 
@@ -22,13 +22,16 @@
 
#include "libdistren.h"
 

	
 
#include "common/protocol.h"
 
#include "common/remoteio.h"
 
#include "common/request.h"
 

	
 
#include <string.h>
 

	
 
static void handle_ping(struct remoteio *rem, struct distren_request *req, void *req_data);
 
static void handle_pong(distren_t distren, struct remoteio *rem, struct distren_request *req, void *req_data);
 
static void handle_version(distren_t distren, struct distren_request *req, void *req_data);
 
static void handle_disconnect(distren_t distren, struct remoteio *rem, struct distren_request *req, void *req_data);
 

	
 
size_t libdistren_remoteio_read_handle(struct remoteio *rem, void *garbage, void *buf, size_t len, distren_t distren)
 
{
 
  size_t to_return;
 
@@ -69,12 +72,16 @@ size_t libdistren_remoteio_read_handle(s
 
      switch((enum distren_request_type)req->type)
 
	{
 
	case DISTREN_REQUEST_PING:
 
	  handle_ping(rem, req, req_data);
 
	  break;
 

	
 
	case DISTREN_REQUEST_PONG:
 
	  handle_pong(distren, rem, req, req_data);
 
	  break;
 

	
 
	case DISTREN_REQUEST_VERSION:
 
	  handle_version(distren, req, req_data);
 
	  break;
 

	
 
	case DISTREN_REQUEST_DISCONNECT:
 
	  handle_disconnect(distren, rem, req, req_data);
 
@@ -115,12 +122,21 @@ static void handle_ping(struct remoteio 
 

	
 
  distren_request_poing(&pong_req, &pong_req_data, 0, req_data, req->len);
 
  distren_request_send(rem, pong_req, pong_req_data);
 
  distren_request_free_with_data(pong_req, pong_req_data);
 
}
 

	
 
static void handle_pong(distren_t distren, struct remoteio *rem, struct distren_request *req, void *req_data)
 
{
 
  const char *auth_test_str = "auth test";
 

	
 
  if(req->len == strlen(auth_test_str)
 
     && !strncmp(req_data, auth_test_str, req->len))
 
    distren->state = DISTREN_STATE_NORMAL;
 
}
 

	
 
static void handle_version(distren_t distren, struct distren_request *req, void *req_data)
 
{
 
  struct distren_request_version version;
 
  int tmp;
 

	
 
  tmp = distren_request_parse_version(req, req_data, &version);
src/common/libremoteio.h
Show inline comments
 
@@ -46,12 +46,13 @@ enum remoteio_method
 

	
 
struct remoteio_server
 
{
 
  char *name; /*< The friendly named passed to remoteio_open() */
 
  char *hostname;
 
  char *username;
 
  char *password;
 
  enum remoteio_method method;
 
  unsigned int types; /*< See ``Server types'' in protocol.h */
 
};
 

	
 
struct remoteio_opts
 
{
src/common/options.c
Show inline comments
 
@@ -170,12 +170,13 @@ int options_init(int argc, char *argv[],
 
    In these arrays, I should replace NULL with a pointer to a string in the struct which should hold the result of confuse processing an option. This is partially because confuse will not free the values it parses.
 
    the server sections in the distrencommon.conf describe different servers that the client may connect to
 
   */
 
  cfg_opt_t server_opts[] =
 
    {
 
      CFG_STR("username", NULL, CFGF_NONE),
 
      CFG_STR("password", NULL, CFGF_NONE),
 
      CFG_STR("hostname", NULL, CFGF_NONE),
 
      CFG_STR("method", "ssh", CFGF_NONE),
 
      CFG_STR_LIST("types", NULL, CFGF_NONE),
 
      CFG_END()
 
    };
 

	
src/common/protocol.h
Show inline comments
 
@@ -58,18 +58,34 @@ enum distren_request_type
 
     * DATA: struct distren_request followed by PACKAGE_STRING. The length of PACKAGE_STRING must be no longer than 32 bytes.
 
     *
 
     * REQUIRED: ALL
 
    */
 
    DISTREN_REQUEST_VERSION = 1,
 
    /**
 
     * Test if the end has a live server.
 
     *
 
     * Only authenticated clients may use this request. Thus, if a
 
     * client wants to confirm that a DISTREN_REQUEST_PASS request was
 
     * successful, that client may send a DISTREN_REQUEST_PING
 
     * immediately after the DISTREN_REQUEST_PASS and then wait for
 
     * the PONG.
 
     *
 
     * DATA: up to 32 bytes of a PING cookie
 
     *
 
     * REQUIRED: ALL
 
     */
 
    DISTREN_REQUEST_PING = 2,
 
    /**
 
     * Response to DISTREN_REQUEST_PING.
 
     *
 
     * Unauthenticated clients may be penalized for responding to PING
 
     * requests. This is because a newly connecting client should
 
     * queue a DISTREN_REQUEST_VERSION and DISTREN_REQUEST_PASS
 
     * back-to-back before checking for and processing data from the
 
     * remote server.
 
     *
 
     * DATA: up to 32 bytes copied from a received PING cookie
 
     *
 
     * REQUIRED: ALL
 
     */
 
    DISTREN_REQUEST_PONG = 3,
 
    /**
 
@@ -81,65 +97,84 @@ enum distren_request_type
 
     * termination.
 
     *
 
     * REQUIRED: ALL
 
     */
 
    DISTREN_REQUEST_DISCONNECT = 4,
 

	
 

	
 
    /**
 
     * Allow a client to identify itself using simple password
 
     * authentication.
 
     *
 
     * As there is no distren request which affirms a
 
     * DISTREN_REQUEST_PASS went through, clients may send a
 
     * DISTREN_REQUEST_PING and wait for the DISTREN_REQUETS_PONG they
 
     * want to block until they're authenticated.
 
     *
 
     * DATA: struct distren_request_pass
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT (for now, since
 
     * server2server links are only protected using password
 
     * authentication, all server types have to support this except
 
     * for the client.)
 
     */
 
    DISTREN_REQUEST_PASS = 5,
 

	
 
    /**
 
     * DATA: struct distren_request_submit
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT
 
     */
 
    DISTREN_REQUEST_SUBMIT = 5,
 
    DISTREN_REQUEST_SUBMIT = 6,
 

	
 
    /**
 
     * Inform the other party about a job.
 
     *
 
     * DATA: struct distren_request_jobinfo
 
     *
 
     * REQUIRED: ALL
 
     */
 
    DISTREN_REQUEST_JOBINFO = 6,
 
    DISTREN_REQUEST_JOBINFO = 7,
 

	
 
    /**
 
     * Request a DISTREN_REQUEST_JOBINFO
 
     *
 
     * DATA: struct distren_request_jobinfo_get
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_JOBINFO_GET = 7,
 
    DISTREN_REQUEST_JOBINFO_GET = 8,
 

	
 
    /**
 
     * Command the other party to render a frame
 
     *
 
     * DATA: struct distren_request_frame_render
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_RENDER
 
     */
 
    DISTREN_REQUEST_FRAME_RENDER = 8,
 
    DISTREN_REQUEST_FRAME_RENDER = 9,
 
    /**
 
     * Inform the receiver of the sender's state, such as frames being
 
     * rendered or jobs that need to be completed.
 
     *
 
     * DATA: struct distren_request_status
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_RENDER, DISTREN_SERVERTYPE_DISTRIBUTE, DISTREN_SERVERTYPE_CLIENT
 
     */
 
    DISTREN_REQUEST_STATUS = 9,
 
    DISTREN_REQUEST_STATUS = 10,
 

	
 
    /**
 
     * Request that the receiver send a DISTREN_REQUEST_FRAME_STATUS
 
     * packet. No data because the sending party might not beforehand
 
     * know what frames/jobs the receiving server is managing.
 
     *
 
     * DATA: (none)
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_RENDER
 
     */
 
    DISTREN_REQUEST_STATUS_GET = 10,
 
    DISTREN_REQUEST_STATUS_GET = 11,
 

	
 
    /**
 
     * Declare that a client is preparing to post a file using a
 
     * particular post-id and with a user-friendly filename.
 
     *
 
     * Yes, this is evil. Yes, this tries to replicate
 
@@ -154,13 +189,13 @@ enum distren_request_type
 
     * Possible future expansions: ``transparent'' zlib compression.
 
     *
 
     * DATA: struct distren_request_file_post_start
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_POST_START = 11,
 
    DISTREN_REQUEST_FILE_POST_START = 12,
 

	
 
    /**
 
     * Allow a client to upload a job tarball over a remoteio line.  A
 
     * client that wants to do this must take care not to overbuffer
 
     * his sendqueue so as to be able to respond to PING packets in
 
     * time. A server receiving such a message will want to write the
 
@@ -179,43 +214,43 @@ enum distren_request_type
 
     * the nasty security issues start coming into play :-D.
 
     *
 
     * DATA: struct distren_request_file_post followed by a maximum of 131072 bytes (128kB).
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_POST = 12,
 
    DISTREN_REQUEST_FILE_POST = 13,
 

	
 
    /**
 
     * Marks a post-id's file as having completely uploaded. Provides
 
     * verification information so that the file's integrity may be
 
     * verified.
 
     *
 
     * DATA: struct distren_request_file_post_finish
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_POST_FINISH = 13,
 
    DISTREN_REQUEST_FILE_POST_FINISH = 14,
 

	
 
    /**
 
     * Request information about obtaining a file (such as a
 
     * cURL-compatible URL) based on a distren file URL.
 
     *
 
     * DATA: struct distren_request_file_find
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_FIND = 14,
 
    DISTREN_REQUEST_FILE_FIND = 15,
 

	
 
    /**
 
     * Provide information about obtaining a file (such as a URL).
 
     *
 
     * DATA: struct distren_request_file
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE = 15,
 
    DISTREN_REQUEST_FILE = 16,
 
  };
 

	
 
struct distren_request
 
{
 
  uint32_t magic;
 
  /* the length of the data associated with the packet excluding the header */
 
@@ -233,12 +268,20 @@ struct distren_request_version
 
{
 
  uint32_t servertype;
 
  /* + 1 is for terminating NULL */
 
  char package_string[DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN + 1];
 
};
 

	
 
#define DISTREN_REQUEST_PASS_USERNAME_LEN (16)
 
#define DISTREN_REQUEST_PASS_PASS_LEN (32)
 
struct distren_request_pass
 
{
 
  char username[DISTREN_REQUEST_PASS_USERNAME_LEN];
 
  char pass[DISTREN_REQUEST_PASS_PASS_LEN];
 
};
 

	
 
#define DISTREN_REQUEST_FILE_POST_NAME_LEN (64)
 
struct distren_request_file_post_start
 
{
 
  /**
 
   * Uniquely identify this file upload (per connection).
 
   */
src/common/remoteio.c
Show inline comments
 
@@ -138,16 +138,23 @@ int remoteio_config(cfg_t *cfg, struct r
 
  for(counter = 0; counter < numservers; counter ++)
 
    {
 
      cfg_t *cfg_aserver;
 
      char *method;
 
      
 
      cfg_aserver = cfg_getnsec(cfg, "server", counter);
 

	
 
      aserver.name = NULL;
 
      aserver.hostname = NULL;
 
      aserver.username = NULL;
 
      aserver.password = NULL;
 
      
 
      aserver.name = strdup(cfg_title(cfg_aserver));
 
      aserver.hostname = strdup(cfg_getstr(cfg_aserver, "hostname"));
 
      aserver.username = strdup(cfg_getstr(cfg_aserver, "username"));
 
      if(cfg_getstr(cfg_aserver, "password"))
 
	aserver.password = strdup(cfg_getstr(cfg_aserver, "password"));
 

	
 
      aserver.method = REMOTEIO_METHOD_MAX;
 
      method = cfg_getstr(cfg_aserver, "method");
 
      for(counter2 = 0; funcmap[counter2].name; counter2 ++)
 
	if(strcmp(method, funcmap[counter2].name) == 0)
 
	  aserver.method = funcmap[counter2].method;
 
@@ -285,12 +292,32 @@ int remoteio_open_server(struct remoteio
 
   */
 
  multiio_socket_add(opts->multiio, rem->sock, opts->socket_type, rem, POLLIN);
 
  
 
  return 0;
 
}
 

	
 
int remoteio_authinfo_get(struct remoteio_opts *rem_opts, const char *servername, const char **username, const char **pass)
 
{
 
  struct remoteio_server *server;
 

	
 
  *username = NULL;
 
  *pass = NULL;
 

	
 
  server = remoteio_getserver(rem_opts, servername);
 
  if(!server)
 
    {
 
      fprintf(stderr, "%s:%d: Could not find server named ``%s''\n", __FILE__, __LINE__, servername);
 
      return 1;
 
    }
 

	
 
  *username = server->username;
 
  *pass = server->password;
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * Implementation of multiio_event_handler_func_t
 
 */
 
int _remoteio_handle_read(multiio_context_t multiio,
 
			  int fd,
 
			  short revent,
src/common/remoteio.h
Show inline comments
 
@@ -117,12 +117,27 @@ int remoteio_open_socket(struct remoteio
 
 * @param len number of bytes to grab from buf
 
 * @return 0 on success, 1 on failure
 
 */
 
int remoteio_write(struct remoteio *rem, const void *buf, size_t len);
 

	
 
/**
 
 * \brief Retrieves authentication information associated with a
 
 *   server's configuration entry.
 
 *
 
 * Possibly, the whole idea of remoteio handling server names with the
 
 * remoteio_open() function should be moved somewhere else and
 
 * remoteio should be more general-purpose I/O?
 
 *
 
 * \param rem_opts A remoteio options handle.
 
 * \param servername The name of the server whose information should be retrieved.
 
 * \param username Where to store a pointer to the username. Do not free this.
 
 * \param pass Where to store a pointer to the password. Do not free this.
 
 */
 
int remoteio_authinfo_get(struct remoteio_opts *rem_opts, const char *servername, const char **username, const char **pass);
 

	
 
/**
 
 * Closes a remoteio session.
 
 *
 
 * It is safe to call this function from within
 
 * remoteio_read_handle_func_t.
 
 *
 
 * @return nonzero on error
src/common/request.c
Show inline comments
 
@@ -66,12 +66,41 @@ int distren_request_parse_version(struct
 
  /* there is space for another '\0' */
 
  version->package_string[DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN] = '\0';
 

	
 
  return 0;
 
}
 

	
 
int distren_request_pass(struct distren_request **req, void **data, const char *username, const char *pass)
 
{
 
  struct distren_request_pass *request_pass;
 

	
 
  distren_request_new(req, sizeof(struct distren_request_pass), DISTREN_REQUEST_PASS);
 
  request_pass = malloc(sizeof(struct distren_request_pass));
 
  if(!request_pass || !*req)
 
    {
 
      if(*req)
 
	distren_request_free(*req);
 
      free(request_pass);
 

	
 
      return 1;
 
    }
 

	
 
  /*
 
   * The packet itself doesn't need the string's NULL terminator
 
   * _unless_ if the string is shorter than the maximum length of the
 
   * username or password. Thus, strncpy()'s behavior (not strlcpy()'s
 
   * behavior) is _exactly_ what we want for this case.
 
   */
 
  strncpy(request_pass->username, username, DISTREN_REQUEST_PASS_USERNAME_LEN);
 
  strncpy(request_pass->pass, pass, DISTREN_REQUEST_PASS_PASS_LEN);
 

	
 
  *data = request_pass;
 

	
 
  return 0;
 
}
 

	
 
int distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len)
 
{
 
  enum distren_request_type type;
 

	
 
  if(is_ping)
 
    type = DISTREN_REQUEST_PING;
src/common/request.h
Show inline comments
 
@@ -53,12 +53,22 @@ int distren_request_version(struct distr
 
 * @param version where the result should be stored.
 
 * @return 0 on success, 1 if the packet is invalid (if the length of package_version is longer than 32-bytes, for example).
 
 */
 
int distren_request_parse_version(struct distren_request *req, void *data, struct distren_request_version *version);
 

	
 
/**
 
 * Initialize a PASS request so as to identify to a server.
 
 *
 
 * \param req Where to store the newly allocated req.
 
 * \param data Where to store the newly allocated data.
 
 * \param user The username to send to the server.
 
 * \param pass The password to send to the server.
 
 */
 
int distren_request_pass(struct distren_request **req, void **data, const char *username, const char *pass);
 

	
 
/**
 
 * Initialize a PING or PONG request.
 
 *
 
 * @param data a place to allocate storage for the data associated with this request
 
 * @param is_ping 1 if this is a DISTREN_REQUEST_PING or 0 if this is a DISTREN_REQUEST_PONG
 
 * @param poing_cookie chocolate chip, chocolate chunk, or oatmeal chocolate chip
 
 * @param poing_data_len bytes in the poing_cookie
src/server/distrend.c
Show inline comments
 
@@ -21,14 +21,15 @@
 
/* This file contains the code which both processes (renders) jobs as a slave, and the code which distributes frames to slaves after receiving them from the client portion of the codebase. */
 

	
 
#include "common/config.h"
 

	
 
#include "distrenjob.h"
 
#include "listen.h"
 
#include "mysql.h"
 
#include "slavefuncs.h"
 
#include "mysql.h"
 
#include "user_mgr.h"
 

	
 
#include "common/asprintf.h"
 
#include "common/execio.h"
 
#include "common/options.h"
 
#include "common/protocol.h"
 
#include "common/request.h"
 
@@ -59,12 +60,18 @@ struct general_info
 
  struct distrend_config *config;
 

	
 
  struct
 
  {
 
    /** general_info.xml */
 
    char *geninfo;
 

	
 
    /**
 
     * \brief Where to store the user listing.
 
     */
 
    char *userlist;
 

	
 
    /**
 
     * where to store in-progress uploads or things that should
 
     * otherwise be on the same filesystem as the rest of the datadir
 
     * so that it may be rename()d.
 
     */
 
    char *tmpdir;
 
@@ -78,12 +85,14 @@ struct general_info
 
  unsigned int total_frames_rendered;
 
  unsigned int highest_jobnum;
 
  int hibernate;
 
  time_t timestamp;
 
  unsigned long total_render_power;
 
  unsigned long total_priority_pieces;
 

	
 
  user_mgr_t user_mgr;
 
};
 

	
 

	
 
/* *********************************************
 
   Function Prototypes
 
   ********************************************* */
 
@@ -95,12 +104,13 @@ int distrend_config_free(struct distrend
 
int distrend_handle_request(struct distrend_listens *listens, struct distrend_client *client, struct distren_request *req, void *reqdata, struct general_info *geninfo);
 

	
 
/**
 
 * client request handlers
 
 */
 
int distrend_handle_version(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 
int distrend_handle_pass(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 
int distrend_handle_file_post_start(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 
int distrend_handle_file_post(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 
int distrend_handle_file_post_finish(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 

	
 
/* functions of some generic sort ...ish */
 
int distrend_handle_successful_upload(struct distrend_client *client, struct distrend_client_file_post *client_file_post);
 
@@ -168,12 +178,24 @@ Ethan Zonca <e@ethanzonca.com>\n\
 
		    general_info.config->datadir);
 

	
 
  _distren_asprintf(&general_info.files.tmpdir, "%s/tmp",
 
		    general_info.config->datadir);
 
  distren_mkdir_recurse(general_info.files.tmpdir);
 

	
 
  _distren_asprintf(&general_info.files.userlist, "%s/users.xml",
 
		    general_info.config->datadir);
 

	
 
  /** configuraton stuff that depends on the paths being calculated, such as loading data */
 
  general_info.user_mgr = user_mgr_init(general_info.files.userlist);
 
  if(!general_info.user_mgr)
 
    {
 
      fprintf(stderr, "Error initializing user_mgr\n");
 
      return 1;
 
    }
 

	
 

	
 
  /** MySQL Connection */
 
  fprintf(stderr,"Connecting to mysql...\n");
 
  if(mysqlConnect(&general_info.conn,
 
		  general_info.config->mysql_user,
 
		  general_info.config->mysql_host,
 
		  general_info.config->mysql_pass,
 
@@ -203,16 +225,18 @@ Ethan Zonca <e@ethanzonca.com>\n\
 
	{
 
	  fprintf(stderr, "Error listening on port %d\n", general_info.config->listen_ports[counter]);
 
	  return 1;
 
	}
 
    }
 

	
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_VERSION, &distrend_handle_version);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST_START, &distrend_handle_file_post_start);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST, &distrend_handle_file_post);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST_FINISH, &distrend_handle_file_post_finish);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_VERSION, &distrend_handle_version, (uint8_t)DISTREND_CLIENT_PREVERSION);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_PASS,
 
			      &distrend_handle_pass, (uint8_t)DISTREND_CLIENT_PREAUTH);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST_START, &distrend_handle_file_post_start, (uint8_t)DISTREND_CLIENT_GOOD);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST, &distrend_handle_file_post, (uint8_t)DISTREND_CLIENT_GOOD);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST_FINISH, &distrend_handle_file_post_finish, (uint8_t)DISTREND_CLIENT_GOOD);
 

	
 
  /* Main Loop */
 
  general_info.config->die = 0;
 
  while(!general_info.config->die)
 
    {
 
      multiio_poll(multiio, 15000);
 
@@ -227,12 +251,14 @@ Ethan Zonca <e@ethanzonca.com>\n\
 
  distrend_config_free(general_info.config);
 

	
 
  xmlcleanup();
 

	
 
  /** free() paths */
 
  free(general_info.files.geninfo);
 
  free(general_info.files.tmpdir);
 
  free(general_info.files.userlist);
 
  mysqlDisconnect(general_info.conn);
 

	
 
  return 0;
 
}
 

	
 
/* ********************** Functions ************************* */
 
@@ -282,12 +308,51 @@ int distrend_handle_version(struct gener
 
    }
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * Handle a DISTREN_REQUEST_PASS request.
 
 */
 
int distrend_handle_pass(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data)
 
{
 
  struct distren_request_pass *pass_req;
 

	
 
  char username[DISTREN_REQUEST_PASS_USERNAME_LEN + 1];
 
  char pass[DISTREN_REQUEST_PASS_PASS_LEN + 1];
 

	
 
  struct user *user;
 

	
 
  if(req->len < sizeof(struct distren_request_pass))
 
    {
 
      distrend_send_disconnect(client, "You tried to send too short of a DISTREN_REQUEST_PASS.");
 
      return 1;
 
    }
 

	
 
  pass_req = req_data;
 
  memcpy(username, pass_req->username, DISTREN_REQUEST_PASS_USERNAME_LEN);
 
  username[DISTREN_REQUEST_PASS_USERNAME_LEN] = '\0';
 

	
 
  memcpy(pass, pass_req->pass, DISTREN_REQUEST_PASS_PASS_LEN);
 
  pass[DISTREN_REQUEST_PASS_PASS_LEN] = '\0';
 

	
 
  user = user_find(geninfo->user_mgr, username);
 
  if(!user
 
     || strcmp(user->username, username)
 
     || strcmp(user->pass, pass))
 
    {
 
      distrend_send_disconnect(client, "Invalid username or password.");
 
      return 1;
 
    }
 

	
 
  client->state = DISTREND_CLIENT_GOOD;
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * Traversal helper for distrend_client_find_post().
 
 */
 
int distrend_client_find_post_traverse(uint32_t *post_id, struct distrend_client_file_post *client_file_post)
 
{
 
  if(*post_id == client_file_post->post_id)
 
    return FALSE;
src/server/listen.c
Show inline comments
 
@@ -39,12 +39,17 @@
 
/* local */
 

	
 
struct distrend_request_handler_info
 
{
 
  enum distren_request_type request_type;
 
  distrend_handle_request_func_t handler;
 
  /**
 
   * A bitmasking of different enum distren_client_states which are
 
   * allowed to use this particular command.
 
   */
 
  uint8_t client_states;
 
};
 

	
 
struct distrend_client *distrend_client_new(struct distrend_listens *listens,
 
					    enum distrend_client_state state,
 
					    struct remoteio *rem,
 
					    int connection_id);
 
@@ -274,13 +279,12 @@ struct distrend_accept_client_proc_data
 
 */
 
int listen_handle_accept(multiio_context_t multiio,
 
			 int fd,
 
			 short revent,
 
			 struct distrend_listens *listens,
 
			 int *port)
 
   //int distrend_accept(struct distrend_listens *listens)//, struct distrend_clientset *clients, distrend_handle_request_t handlereq, void *handlereqdata)
 
 {
 
   struct distrend_client *newclient;
 

	
 
   int newclientsock;
 

	
 
   struct remoteio *rem;
 
@@ -358,12 +362,18 @@ size_t distrend_listen_read_handle(struc
 
 {
 
   struct distren_request *req;
 
   void *reqdata;
 

	
 
   size_t used_len;
 

	
 
  /*
 
   * ignore packets from a client who's going to be disconnected.
 
   */
 
  if(client->state == DISTREND_CLIENT_BAD)
 
    return len;
 

	
 
   used_len = 0;
 
   /**
 
    * Manage input, etc.
 
    */
 
   if(client->expectlen == 0)
 
     {
 
@@ -552,22 +562,23 @@ int distrend_send_disconnect(struct dist
 
  client->state = DISTREND_CLIENT_BAD;
 
  client->cleanup_time = time(NULL) + DISTREND_LISTEN_DISCONNECT_GRACE;
 

	
 
  return 0;
 
}
 

	
 
int distrend_listen_handler_add(struct distrend_listens *listens, enum distren_request_type type, distrend_handle_request_func_t handler)
 
int distrend_listen_handler_add(struct distrend_listens *listens, enum distren_request_type type, distrend_handle_request_func_t handler, uint8_t client_state_mask)
 
{
 
  struct distrend_request_handler_info *handler_info;
 

	
 
  handler_info = malloc(sizeof(struct distrend_request_handler_info));
 
  if(!handler_info)
 
    return 1;
 

	
 
  handler_info->request_type = type;
 
  handler_info->handler = handler;
 
  handler_info->client_states = client_state_mask;
 
  list_insert_after(listens->request_handlers, handler_info, 0);
 

	
 
  return 0;
 
}
 

	
 
struct distrend_dispatch_request_data
 
@@ -581,13 +592,25 @@ struct distrend_dispatch_request_data
 
/**
 
   traversal function for distrend_dispatch_request().
 
 */
 
int _distrend_dispatch_request_trav(struct distrend_dispatch_request_data *data, struct distrend_request_handler_info *handler_info)
 
{
 
  if(handler_info->request_type == data->req->type)
 
    (*handler_info->handler)(data->geninfo, data->client, data->req, data->req_data);
 
    {
 
      /* check permissions first */
 
      if(!(data->client->state & handler_info->client_states))
 
	{
 
	  distrend_send_disconnect(data->client, "You attempted to use a command out of context.");
 
	  return FALSE;
 
	}
 

	
 
      (*handler_info->handler)(data->geninfo, data->client, data->req, data->req_data);
 

	
 
      /* shortcut one a hit ;-) */
 
      return FALSE;
 
    }
 

	
 
  return TRUE;
 
}
 

	
 
/**
 
   helper for distrend_listen_read_handle() which looks up the correct
src/server/listen.h
Show inline comments
 
@@ -56,33 +56,37 @@ struct distrend_client;
 
   his connection is dropped. This grace time is intended so that
 
   the client will actually see his disconnect message instead of
 
   just having his connection reset.
 
 */
 
#define DISTREND_LISTEN_DISCONNECT_GRACE 8
 

	
 
/**
 
 * Numbers are explicitly given so that commands can be restricted to
 
 * clients in certain states.
 
 */
 
enum distrend_client_state
 
  {
 
    /**
 
       The client hasn't yet given us its version.
 
     */
 
    DISTREND_CLIENT_PREVERSION,
 
    DISTREND_CLIENT_PREVERSION = 1,
 
    /**
 
       We don't yet know the client. It may only use authentication
 
       commands.
 
     */
 
    DISTREND_CLIENT_PREAUTH,
 
    DISTREND_CLIENT_PREAUTH = 2,
 
    /**
 
       The client is authenticated, etc.
 
     */
 
    DISTREND_CLIENT_GOOD,
 
    DISTREND_CLIENT_GOOD = 4,
 
    /**
 
       The client is queued to be disconnected. (This state exists
 
       so that the client at least has a chance to recieve its
 
       disconnect message/error before being dumped).
 
     */
 
    DISTREND_CLIENT_BAD,
 
    DISTREND_CLIENT_BAD = 8,
 
  };
 

	
 
struct distrend_listens
 
{
 
  /* of type (struct distrend_request_handler_info) */
 
  list_t request_handlers;
 
@@ -185,17 +189,21 @@ struct distrend_listens *distrend_listen
 
 */
 
int distrend_listen_add(struct distrend_listens *listens, int port);
 

	
 
/**
 
 * Register a request handler with the listener.
 
 *
 
 * @param config distrend's configuration
 
 * @param type the request type for which this handler should be called
 
 * @param handler the handler to call when a request of type type is received.
 
 * \param config distrend's configuration.
 
 * \param type The request type for which this handler should be
 
 *   called
 
 * \param handler The handler to call when a request of type type is
 
 *   received.
 
 * \param client_state_mask A mask of enum distrend_client_state
 
 *   values that are allowed to use this command.
 
 */
 
int distrend_listen_handler_add(struct distrend_listens *listens, enum distren_request_type type, distrend_handle_request_func_t handler);
 
int distrend_listen_handler_add(struct distrend_listens *listens, enum distren_request_type type, distrend_handle_request_func_t handler, uint8_t client_state_mask);
 

	
 
/**
 
 * cleans listening sockets/frees main struct. Unnecessary for a working server, currently a stub.
 
 */
 
int distrend_listen_free(struct distrend_listens *listens);
 

	
src/server/tabletennis.c
Show inline comments
 
@@ -57,14 +57,14 @@ tabletennis_t tabletennis_new(struct dis
 
  tabletennis->ping_interval = ping_interval;
 
  tabletennis->pong_time = pong_time;
 
  tabletennis->clients_to_ping = q_init();
 
  tabletennis->clients_need_pong = q_init();
 
  clock_gettime(CLOCK_MONOTONIC, &tabletennis->time_last_check);
 

	
 
  distrend_listen_handler_add(listens, DISTREN_REQUEST_PING, &tabletennis_ping_request_handle);
 
  distrend_listen_handler_add(listens, DISTREN_REQUEST_PONG, &tabletennis_pong_request_handle);
 
  distrend_listen_handler_add(listens, DISTREN_REQUEST_PING, &tabletennis_ping_request_handle, (uint8_t)DISTREND_CLIENT_GOOD);
 
  distrend_listen_handler_add(listens, DISTREN_REQUEST_PONG, &tabletennis_pong_request_handle, (uint8_t)DISTREND_CLIENT_GOOD);
 

	
 
  return tabletennis;
 
}
 

	
 
int tabletennis_add_client(tabletennis_t tabletennis, struct distrend_client *client)
 
{
src/server/user_mgr.c
Show inline comments
 
/*
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  This file is a part of DistRen.
 

	
 
  DistRen is free software: you can redistribute it and/or modify
 
  it under the terms of the GNU Affero General Public License as published by
 
  the Free Software Foundation, either version 3 of the License, or
 
  (at your option) any later version.
 

	
 
  DistRen is distributed in the hope that it will be useful,
 
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  GNU Affero General Public License for more details.
 

	
 
  You should have received a copy of the GNU Affero General Public License
 
  along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 
 *  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
 *
 
 * This file is a part of DistRen.
 
 *
 
 * DistRen is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * DistRen is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#include "common/config.h"
 

	
 
#include "user_mgr.h"
 

	
 
#include "common/asprintf.h"
 

	
 
#include <list.h>
 

	
 
#include <libxml/xmlmemory.h>
 
#include <libxml/parser.h>
 
#include <libxml/tree.h>
 
#include <libxml/encoding.h>
 
#include <libxml/xmlwriter.h>
 
@@ -34,308 +35,291 @@
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <unistd.h>
 
#include <sys/stat.h>
 

	
 
struct user_mgr_info
 
struct user_mgr
 
{
 
	struct user *user_array;
 
	int current_users;
 
	int user_array_size;
 
} user_mgr_info;
 

	
 
int resize_user_array()
 
{
 
	int counter;
 
	int counter2;
 

	
 
	// create array twice the size of the current amount of users
 
	user_mgr_info.user_array_size = user_mgr_info.current_users * 2;
 
	struct user *new_user_array = malloc(sizeof(struct user) * user_mgr_info.user_array_size);
 
  /* items are of type user_t */
 
  list_t user_list;
 
  /* where to load/save the userlist */
 
  char *userlist_filename;
 
};
 

	
 
	// this copies the original user_array over to the new one
 
	// using two counters allows the array to be resized at any time
 
	// leaving exactly 1 open space between each user when it is done;
 
	counter2 = 0;
 
	for(counter = 0; counter < user_mgr_info.current_users; counter++)
 
	{
 
		if(user_mgr_info.user_array[counter].username != 0)
 
		{
 
			new_user_array[counter2*2] = user_mgr_info.user_array[counter];
 
			counter2++;
 
		}
 
	}
 
static int user_find_traverse_forw(char *search_username, user_t user)
 
{
 
  if(strcmp(search_username, user->username) >= 0)
 
    return FALSE;
 
  return TRUE;
 
}
 

	
 
	// cleanup old array
 
	free(user_mgr_info.user_array);
 

	
 
	// change the pointer to point to the new user array
 
	user_mgr_info.user_array = new_user_array;
 

	
 
	return 1;
 
static int user_find_traverse_back(char *search_username, user_t user)
 
{
 
  if(strcmp(search_username, user->username) <= 0)
 
    return FALSE;
 
  return TRUE;
 
}
 

	
 
struct user *findUser(char *nameOfUser)
 
user_t user_find(user_mgr_t user_mgr, const char *username)
 
{
 
	int high;
 
	int low;
 
	int middle;
 
	int result;
 
  int list_direction;
 
  list_traverse_func_t traverse_func;
 

	
 
  user_t user;
 
  char *username_copy;
 

	
 
	high = user_mgr_info.user_array_size - 1;
 
	low = 0;
 
	result = -1;
 
  if(list_empty(user_mgr->user_list))
 
    return NULL;
 

	
 
  /* grab the current user for tiny optimizations.. */
 
  user = list_curr(user_mgr->user_list);
 
  if(!user)
 
    return NULL;
 

	
 
	for(middle = (low+high)/2; 1 == 1; middle = (low+high)/2)
 
	{
 
		// in case middle lands on a part of the array with no user
 
		while(user_mgr_info.user_array[middle].username == 0)
 
		{
 
			if(result < 0)
 
				middle --;
 
			else
 
				middle ++;
 
		}
 
  list_direction = LIST_FORW;
 
  traverse_func = (list_traverse_func_t)&user_find_traverse_forw;
 
  if(strcmp(user->username, username) < 0)
 
    {
 
      list_direction = LIST_BACK;
 
      traverse_func = (list_traverse_func_t)&user_find_traverse_back;
 
    };
 

	
 
		// this is where the array is cut in half and the half that the nameOfUser is on is kept
 
		result = strcmp(nameOfUser, user_mgr_info.user_array[middle].username);
 
		if(result == 0)
 
			return &user_mgr_info.user_array[middle];
 
		else if(result < 0)
 
			high = middle;
 
		else
 
			low = middle;
 
  username_copy = strdup(username);
 
  list_traverse(user_mgr->user_list, username_copy, traverse_func, list_direction|LIST_CURR|LIST_ALTR);
 
  free(username_copy);
 
  user = list_curr(user_mgr->user_list);
 
  if(!user)
 
    return NULL;
 

	
 
		// in case the user doesn't exist
 
		if(high-low <= 0)
 
			return 0;
 
	}
 
  if(!strcmp(username, user->username))
 
    return user;
 

	
 
  return NULL;
 
}
 

	
 
int deleteUser(struct user *user_ptr)
 
int user_add(user_mgr_t user_mgr, const char *username, const char *pass)
 
{
 
	free(user_ptr->username);
 
	memset(user_ptr, '\0', sizeof(struct user));
 
  user_t user;
 
  short insert_after;
 

	
 
	user_mgr_info.current_users--;
 

	
 
	return 1;
 

	
 
	backup_list_XML();
 
}
 
  user = user_find(user_mgr, username);
 
  if(user)
 
    return 1;
 

	
 
int createUser(struct user *user_ptr, char *nameOfUser)
 
{
 
	// clear old memory
 
	memset(user_ptr, '\0', sizeof(struct user));
 
  /*
 
   * The list should be positioned within one element of where we want
 
   * to insert username.
 
   */
 
  insert_after = 1;
 
  user = list_curr(user_mgr->user_list);
 
  if(user)
 
    {
 
      if(strcmp(username, user->username) < 0)
 
	insert_after = 0;
 
    }
 

	
 
	user_ptr->username = nameOfUser;
 
	user_ptr->render_power = 0;
 
	user_ptr->last_job = 0;
 
  /*
 
   * construct the new user.
 
   */
 
  user = malloc(sizeof(struct user));
 
  if(!user)
 
    return 1;
 
  user->username = strdup(username);
 
  user->pass = strdup(pass);
 
  user->render_power = 0;
 
  user->last_job = 0;
 

	
 
	user_mgr_info.current_users++;
 
  /* I admit it... I'm completely crazy --binki */
 
  if(insert_after)
 
    list_insert_after(user_mgr->user_list, user, 0);
 
  else
 
    list_insert_before(user_mgr->user_list, user, 0);
 

	
 
	return 1;
 
  return 0;
 
}
 

	
 
// places the new user at position index in the array, moves other users if it needs to
 
int placeUser(int index, char *nameOfUser)
 
/**
 
 * \brief For list_free() et al
 
 */
 
static void user_free(user_t user)
 
{
 
	int higher;
 
	int lower;
 
	int total_moves;
 

	
 
	total_moves = 0;
 
  free(user->username);
 
  free(user->pass);
 
  free(user);
 
}
 

	
 
	// I shift data in the array to create an open the space where the user should be added
 
	// but first I figure out which way is the shortest
 
	if(user_mgr_info.user_array[index].username != 0)
 
	{
 
		higher = index + 1;
 
		while(user_mgr_info.user_array[higher].username != 0)
 
			higher++;
 

	
 
		lower = index - 1;
 
		while(user_mgr_info.user_array[lower].username != 0)
 
			lower--;
 
int user_delete(user_mgr_t user_mgr, user_t user)
 
{
 
  user_t user_found;
 
  int ret;
 

	
 
		// here the data is shifted to open up a space
 
		if(index - lower < higher - index)
 
		  {
 
		    total_moves = index - lower;
 
		    for(; lower < index; lower++)
 
				memcpy(&user_mgr_info.user_array[lower], &user_mgr_info.user_array[lower + 1], sizeof(struct user));
 
		  }
 
		else
 
		  {
 
		    total_moves = higher - index;
 
		    for(; higher > index; higher--)
 
				memcpy(&user_mgr_info.user_array[higher], &user_mgr_info.user_array[higher - 1], sizeof(struct user));
 
		  }
 
	}
 
  ret = 0;
 

	
 
	// add the user to the array
 
	createUser(&user_mgr_info.user_array[index], nameOfUser);
 
  user_found = user_find(user_mgr, user->username);
 
  if(user_found
 
     && user_found == user
 
     && user == list_curr(user_mgr->user_list))
 
    list_remove_curr(user_mgr->user_list);
 
  else
 
    {
 
      fprintf(stderr, __FILE__ ":%d:user_delete(): List is inconsistent :-/\n", __LINE__);
 
      ret = 1;
 
    }
 

	
 
	if(total_moves > 50)
 
	  resize_user_array();
 
  user_free(user);
 

	
 
	return 1;
 
  return ret;
 
}
 

	
 
int addUser(char *nameOfUser)
 
static int user_mgr_save_traverse(xmlTextWriterPtr writer, user_t user)
 
{
 
	int high;
 
	int low;
 
	int middle;
 
	int result;
 

	
 
	high = user_mgr_info.user_array_size - 1;
 
	low = 0;
 
	result = -1;
 
  char *tmp;
 

	
 
	for(middle = (low+high)/2; 1 == 1; middle = (low+high)/2)
 
	{
 
		// in case middle lands on a part of the array with no user
 
		while(user_mgr_info.user_array[middle].username == 0)
 
		{
 
			if(result < 0)
 
				middle--;
 
			else
 
				middle++;
 
		}
 
  xmlTextWriterStartElement(writer, (xmlChar *)"user");
 

	
 
  xmlTextWriterWriteAttribute(writer, (xmlChar *)"name", (xmlChar*)user->username);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar *)"pass", (xmlChar*)user->pass);
 

	
 
		// this is where the array is cut in half and the half that the nameOfUser is on is kept
 
		result = strcmp(nameOfUser, user_mgr_info.user_array[middle].username);
 
		if(result == 0)
 
			return 0;
 
		else if(result < 0)
 
			high = middle;
 
		else
 
			low = middle;
 

	
 
		// once there are less than 10 possible places for the user to be placed
 
		// break out of this loop and begin next loop
 
		if(high-low <= 10)
 
			break;
 
	}
 
  _distren_asprintf(&tmp, "%d", user->last_job);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar *)"last_job", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
	// this function starts at the low index number, and goes up until it finds a
 
	// username that is bigger than it alphabetically, and tells the userPlacer
 
	// that it needs to go 1 before that spot
 
	for(; low <= high; low++)
 
	{
 
		while(user_mgr_info.user_array[low].username == 0)
 
			low++;
 
  _distren_asprintf(&tmp, "%d", user->render_power);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar *)"render_power", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
		result = strcmp(nameOfUser, user_mgr_info.user_array[low].username) < 0;
 
		if(result < 0)
 
		{
 
			placeUser(low - 1, nameOfUser);
 
			return 1;
 
		}
 
		if(result == 0)
 
		{
 
			fprintf(stderr, "user already exists");
 
			return 0;
 
		}
 
	}
 
  xmlTextWriterEndElement(writer);
 

	
 
	backup_list_XML();
 
	return 0;
 
  return TRUE;
 
}
 

	
 
int initialize_users()
 
int user_mgr_save(user_mgr_t user_mgr, const char *filename)
 
{
 
  struct stat buffer;
 
  // cif user_list.xml exists
 
  if(stat("user_list.xml", &buffer) == 0)
 
  {
 
    restart_From_XML_backup();
 
  }
 
  else
 
  {
 
    user_mgr_info.current_users = 0;
 
  xmlTextWriterPtr writer;
 

	
 
  if(!filename)
 
    filename = user_mgr->userlist_filename;
 

	
 
  writer = xmlNewTextWriterFilename(filename, 0);
 
  xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL);
 

	
 
    user_mgr_info.user_array_size = 50;
 
    user_mgr_info.user_array = malloc(sizeof(struct user) * 50);
 
  }
 
  /*
 
   * create root element user_list
 
   */
 
  xmlTextWriterStartElement(writer, (xmlChar*)"user_list");
 

	
 
  // if XML file is not found create new array of size 50
 
  /**
 
   * \todo error checking/handling?
 
   */
 
  list_traverse(user_mgr->user_list, writer, (list_traverse_func_t)&user_mgr_save_traverse, LIST_FRNT|LIST_FORW|LIST_SAVE);
 

	
 
  xmlTextWriterEndElement(writer);
 
  xmlTextWriterEndDocument(writer);
 
  xmlTextWriterFlush(writer);
 

	
 
  return 1;
 
  return 0;
 
}
 

	
 
/********************************** XMLness *****************************/
 

	
 
int backup_list_XML()
 
user_mgr_t user_mgr_init(const char *userfile)
 
{
 
	xmlTextWriterPtr writer;
 
        char *tmp;
 
	int counter;
 

	
 

	
 
	writer = xmlNewTextWriterFilename("user_list.xml", 0);
 
	xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL);
 

	
 
	// create root element user_list
 
	xmlTextWriterStartElement(writer, (xmlChar*)"user_list");
 

	
 
	_distren_asprintf(&tmp, "%d", user_mgr_info.current_users);
 
	xmlTextWriterWriteAttribute(writer, (xmlChar*)"amount_of_users", (xmlChar*)tmp);
 
	free(tmp);
 

	
 
	for(counter = 0; counter < user_mgr_info.user_array_size; counter++)
 
	{
 
		if(user_mgr_info.user_array[counter].username != 0)
 
		{
 
			xmlTextWriterStartElement(writer, (xmlChar*)"user");
 

	
 
			xmlTextWriterWriteAttribute(writer, (xmlChar*)"name", (xmlChar*)user_mgr_info.user_array[counter].username);
 

	
 
			_distren_asprintf(&tmp, "%d", user_mgr_info.user_array[counter].last_job);
 
			xmlTextWriterWriteAttribute(writer, (xmlChar*)"last_job", (xmlChar*)tmp);
 
			free(tmp);
 

	
 
			_distren_asprintf(&tmp, "%d", user_mgr_info.user_array[counter].render_power);
 
			xmlTextWriterWriteAttribute(writer, (xmlChar*)"render_power", (xmlChar*)tmp);
 
			free(tmp);
 

	
 
			xmlTextWriterEndElement(writer);
 
		}
 
	}
 

	
 
	return 0;
 

	
 
}
 

	
 
int restart_From_XML_backup(){
 
  xmlDocPtr doc;
 
  xmlNodePtr cur;
 
  int counter;
 

	
 
  user_mgr_t user_mgr;
 

	
 
  user_t user;
 
  xmlChar *username;
 
  xmlChar *pass;
 
  xmlChar *tmp;
 
  int render_power;
 
  int last_job;
 

	
 
  doc = xmlParseFile("user_list.xml");
 
  user_mgr = malloc(sizeof(struct user_mgr));
 
  user_mgr->user_list = list_init();
 
  user_mgr->userlist_filename = strdup(userfile);
 

	
 
  doc = xmlParseFile(userfile);
 
  if (!doc)
 
    {
 
      fprintf(stderr, "user_mgr: Error opening userlist, assuming we should start with an empty userlist\n");
 
      return user_mgr;
 
    }
 

	
 
  cur = xmlDocGetRootElement(doc);
 
  if (xmlStrcmp(cur->name, (xmlChar*)"user_list"))
 
    {
 
      fprintf(stderr, "xml document is wrong type");
 
      fprintf(stderr, "user_mgr: xml document is wrong type. Using empty userlist, which will overwrite the old userlist soon probably...");
 
      xmlFreeDoc(doc);
 
      return 1;
 
      return user_mgr;
 
    }
 

	
 
  user_mgr_info.current_users = atoi((char*)xmlGetProp(cur, (xmlChar*)"amount_of_users"));
 
  for(cur = cur->xmlChildrenNode; cur; cur = cur->next)
 
    {
 
      if (cur->type != XML_ELEMENT_NODE)
 
	/* skip the implicit XML_TEXT_NODEs */
 
	continue;
 

	
 
      if (xmlStrcmp(cur->name, (xmlChar *)"user"))
 
	{
 
	  fprintf(stderr, "user_mgr: Unrecognized XML element: <%s />\n",
 
		 cur->name);
 

	
 
	  continue;
 
	}
 

	
 
  user_mgr_info.user_array_size = user_mgr_info.current_users * 2;
 
  user_mgr_info.user_array = malloc(sizeof(struct user) * user_mgr_info.user_array_size);
 
      username = xmlGetProp(cur, (xmlChar *)"name");
 
      pass = xmlGetProp(cur, (xmlChar *)"pass");
 

	
 
      if(!username)
 
	{
 
	  fprintf(stderr, "<user /> is missing a name attribute! (skipping)\n");
 
	  continue;
 
	}
 
      if(!pass)
 
	{
 
	  fprintf(stderr, "<user name=\"%s\"/> is missing a pass attribute! (skipping)\n",
 
		  username ? (char *)username : "");
 
	  continue;
 
	}
 

	
 
  cur = cur->xmlChildrenNode;
 
  for(counter = 0; cur->next; counter++){
 
    user_mgr_info.user_array[counter*2].username = (char*)xmlGetProp(cur, (xmlChar*)"amount_of_users");
 
    user_mgr_info.user_array[counter*2].last_job = atoi((char*)xmlGetProp(cur, (xmlChar*)"last_job"));
 
    user_mgr_info.user_array[counter*2].render_power = atoi((char*)xmlGetProp(cur, (xmlChar*)"render_power"));
 
    cur = cur->next;
 
  }
 
      last_job = 0;
 
      tmp = xmlGetProp(cur, (xmlChar *)"last_job");
 
      if(tmp)
 
	{
 
	  last_job = atoi((char *)tmp);
 
	  xmlFree(tmp);
 
	}
 

	
 
      render_power = 0;
 
      if(tmp)
 
	{
 
	  tmp = xmlGetProp(cur, (xmlChar *)"render_power");
 
	  render_power = atoi((char *)tmp);
 
	  xmlFree(tmp);
 
	}
 

	
 
      user_add(user_mgr, (char *)username, (char *)pass);
 
      xmlFree(username);
 
      xmlFree(pass);
 

	
 
  return 0;
 
      /*
 
       * user_find should be a very inexpensive operation immediately
 
       * after the user_add() above. I just don't want to trust to
 
       * list_curr() right in this function ;-).
 
       *
 
       * Also, we set this information without passing the following
 
       * as arguments to user_add() because user_add() is an external
 
       * API used for when a user is initially added to the
 
       * userlist. Thus, everybody'd be calling it with
 
       * user_add("username", "pass", 0, 0);
 
       */
 
      user = user_find(user_mgr, (char *)username);
 
      if(user)
 
	{
 
	  user->render_power = render_power;
 
	  user->last_job = last_job;
 
	}
 
    }
 

	
 
  return user_mgr;
 
}
src/server/user_mgr.h
Show inline comments
 
@@ -24,16 +24,66 @@
 

	
 
#ifndef _DISTREN_USER_MGR_H
 
#define _DISTREN_USER_MGR_H
 

	
 
struct user
 
{
 
	char *username;
 
	int render_power;
 
	int last_job;
 
  char *username;
 
  char *pass;
 
  int render_power;
 
  int last_job;
 
};
 
typedef struct user *user_t;
 

	
 
struct user_mgr;
 
typedef struct user_mgr *user_mgr_t;
 

	
 
/**
 
 * \brief Allocate and initialize a user_mgr.
 
 *
 
 * \param userfile The path to where and XML file with user
 
 *   information may be found and where the users file should be save
 
 *   to when new users are added.
 
 * \return The new user_mgr or NULL on error.
 
 */
 
user_mgr_t user_mgr_init(const char *userfile);
 

	
 
/**
 
 * \brief Find a user by username.
 
 *
 
 * \param user_mgr The user_mgr...
 
 * \param username The username to search for.
 
 * \return The user if it is in user_mgr or NULL.
 
 */
 
user_t user_find(user_mgr_t user_mgr, const char *username);
 

	
 
/**
 
 * \brief Add a user to the user_mgr.
 
 *
 
 * \param username The user's username (which is local in scope to this server).
 
 * \param pass The user's plaintext password (insecurity is sooo much easier ;-) ).
 
 * \return 0 on success, 1 on failure (attempt to insert duplicate user, etc.).
 
 */
 
int user_add(user_mgr_t user_mgr, const char *username, const char *pass);
 

	
 
/**
 
 * \brief Delete a user.
 
 *
 
 * The user handle passed to this function is no longer valid after
 
 * this function is called.
 
 *
 
 * \param user The user's handle as retrieved via user_find().
 
 * \return 0 on success, 1 on failure (user does not exist, inconsistency, etc.)
 
 */
 
int user_delete(user_mgr_t user_mgr, user_t user);
 

	
 
/**
 
 * \brief Write out the XML file listing all of the users local to this server.
 
 *
 
 * \param filename The file to write the userlist to or NULL if you
 
 *   want to write to the same filename you loaded the usre_mgr from.
 
 */
 
int user_mgr_save(user_mgr_t user_mgr, const char *filename);
 

	
 

	
 
int restart_From_XML_backup();
 
int backup_list_XML();
 

	
 
#endif
0 comments (0 inline, 0 general)