/* -*- c -*-
*
* ----------------------------------------------------------------------
* CcXstream test client for XBOX Media Server
* ----------------------------------------------------------------------
*
* Copyright (c) 2002-2003 by PuhPuh
*
* This code is copyrighted property of the author. It can still
* be used for any non-commercial purpose following conditions:
*
* 1) This copyright notice is not removed.
* 2) Source code follows any distribution of the software
* if possible.
* 3) Copyright notice above is found in the documentation
* of the distributed software.
*
* Any express or implied warranties are disclaimed. Author is
* not liable for any direct or indirect damages caused by the use
* of this software.
*
* ----------------------------------------------------------------------
*
*/
#include "ccincludes.h"
#include "ccbuffer.h"
#include "ccxclient.h"
#include "ccxencode.h"
#ifdef HAVE_READLINE
#include <readline/readline.h>
#endif /* HAVE_READLINE */
const char *av0;
char *discovered_server = NULL;
int discovered_port = 0;
void usage(int exitval);
char *cc_readline(const char *prompt);
char *cc_readline(const char *prompt)
{
if (prompt == NULL)
prompt = "";
#ifdef HAVE_READLINE
{
char *l, *r;
l = readline(prompt);
if (l == NULL)
return NULL;
r = xx_xstrdup(l);
free(l); /* Not cc_xfree here. */
return r;
}
#else /* HAVE_READLINE */
{
char buf[1024];
size_t len;
fputs(prompt, stdout);
fflush(stdout);
if (fgets(buf, sizeof (buf), stdin) == NULL)
return NULL;
len = strlen(buf);
while ((len > 0) && ((buf[len - 1] == '\n') || (buf[len - 1] == '\r')))
{
len--;
buf[len] = '\0';
}
return cc_xstrdup(buf);
}
#endif /* HAVE_READLINE */
}
CcXstreamClientError multi_get_file(CcXstreamServerConnection s, const char *path, int n)
{
unsigned long *handle;
unsigned char *opened, something_done;
CcXstreamClientError rv;
unsigned char *data;
size_t data_len;
int i;
handle = cc_xcalloc(n, sizeof (handle[0]));
opened = cc_xcalloc(n, sizeof (opened[0]));
something_done = 0;
for (i = 0; i < n; i++)
{
rv = cc_xstream_client_file_open(s, path, &(handle[i]));
if (rv == CC_XSTREAM_CLIENT_OK)
{
opened[i] = 1;
something_done = 1;
fprintf(stderr, "+");
}
else if (rv == CC_XSTREAM_CLIENT_FATAL_ERROR)
{
cc_xfree(handle);
cc_xfree(opened);
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
else
{
fprintf(stderr, "-");
}
}
if (! something_done)
{
cc_xfree(handle);
cc_xfree(opened);
return CC_XSTREAM_CLIENT_COMMAND_FAILED;
}
while (1)
{
something_done = 0;
for (i = 0; i < n; i++)
{
if (opened[i])
{
switch (rv = cc_xstream_client_file_read(s, handle[i], 2048, &data, &data_len))
{
case CC_XSTREAM_CLIENT_OK:
if (data_len == 0)
{
fprintf(stderr, "*");
}
else
{
something_done = 1;
}
cc_xfree(data);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "!");
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
cc_xfree(handle);
cc_xfree(opened);
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
}
}
if (! something_done)
goto close_and_return;
}
close_and_return:
for (i = 0; i < n; i++)
{
if (opened[i])
switch (cc_xstream_client_close(s, handle[i]))
{
case CC_XSTREAM_CLIENT_OK:
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
/* Failing here means we are out of sync somehow and it's fatal. */
cc_xfree(handle);
cc_xfree(opened);
return CC_XSTREAM_CLIENT_FATAL_ERROR;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
cc_xfree(handle);
cc_xfree(opened);
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
}
cc_xfree(handle);
cc_xfree(opened);
return CC_XSTREAM_CLIENT_OK;
/*NOTREACHED*/
}
CcXstreamClientError get_file(CcXstreamServerConnection s, const char *path, int print)
{
unsigned long handle;
CcXstreamClientError rv;
unsigned char *data;
size_t data_len;
rv = cc_xstream_client_file_open(s, path, &handle);
if (rv != CC_XSTREAM_CLIENT_OK)
return rv;
while (1)
{
switch (rv = cc_xstream_client_file_read(s, handle, print ? 100 : 2048, &data, &data_len))
{
case CC_XSTREAM_CLIENT_OK:
if (data_len == 0)
{
goto close_and_return;
}
if (print)
{
fwrite(data, 1, data_len, stdout);
}
cc_xfree(data);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
goto close_and_return;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
}
close_and_return:
switch (cc_xstream_client_close(s, handle))
{
case CC_XSTREAM_CLIENT_OK:
return rv;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
/* Failing here means we are out of sync somehow and it's fatal. */
return CC_XSTREAM_CLIENT_FATAL_ERROR;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
/*NOTREACHED*/
}
CcXstreamClientError list_dir(CcXstreamServerConnection s, int full)
{
unsigned long handle;
CcXstreamClientError rv;
char *name, *info;
int i = 0;
rv = cc_xstream_client_dir_open(s, &handle);
if (rv != CC_XSTREAM_CLIENT_OK)
return rv;
while (1)
{
switch (rv = cc_xstream_client_dir_read(s, handle, &name, &info))
{
case CC_XSTREAM_CLIENT_OK:
if (strlen(name) == 0)
{
cc_xfree(name);
cc_xfree(info);
return CC_XSTREAM_CLIENT_OK;
}
if (full)
fprintf(stderr, "%2d) name = \"%s\" info = \"%s\"\n", ++i, name, info);
else
fprintf(stderr, "%2d) %s\n", ++i, name);
cc_xfree(name);
cc_xfree(info);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
goto close_and_return;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
}
close_and_return:
switch (cc_xstream_client_close(s, handle))
{
case CC_XSTREAM_CLIENT_OK:
return rv;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
/* Failing here means we are out of sync somehow and it's fatal. */
return CC_XSTREAM_CLIENT_FATAL_ERROR;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
/*NOTREACHED*/
}
CcXstreamClientError fuzzy_get_file(CcXstreamServerConnection s, const char *path, int print)
{
unsigned long handle;
CcXstreamClientError rv;
unsigned char *data;
size_t data_len, read_len;
int last_read_was_truncated;
rv = cc_xstream_client_file_open(s, path, &handle);
if (rv != CC_XSTREAM_CLIENT_OK)
return rv;
read_len = print ? 100 : 2048;
while (1)
{
/* Read 100 (or 2048 bytes) */
switch (rv = cc_xstream_client_file_read(s, handle, read_len, &data, &data_len))
{
case CC_XSTREAM_CLIENT_OK:
if (data_len == 0)
{
goto close_and_return;
}
if (print)
{
fwrite(data, 1, data_len, stdout);
}
last_read_was_truncated = (data_len != read_len);
cc_xfree(data);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
goto close_and_return;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
/* Seek back 50 (or 1024 bytes) */
if (! last_read_was_truncated)
{
switch (rv = cc_xstream_client_file_backwards(s, handle, read_len / 2, 0))
{
case CC_XSTREAM_CLIENT_OK:
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
goto close_and_return;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
}
}
close_and_return:
switch (cc_xstream_client_close(s, handle))
{
case CC_XSTREAM_CLIENT_OK:
return rv;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
/* Failing here means we are out of sync somehow and it's fatal. */
return CC_XSTREAM_CLIENT_FATAL_ERROR;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
return CC_XSTREAM_CLIENT_FATAL_ERROR;
}
/*NOTREACHED*/
}
void discovery_cb(const char *addr, const char *port, const char *version, const char *comment, void *context)
{
printf("Server: address = \"%s\"\n", addr);
printf(" port = \"%s\"\n", port);
printf(" version = \"%s\"\n", version);
printf(" comment = \"%s\"\n", comment);
if (discovered_server == NULL)
{
discovered_server = cc_xstrdup(addr);
discovered_port = atoi(port);
}
}
CcXstreamClientError discover()
{
return ccx_client_discover_servers(discovery_cb, NULL);
}
void usage(int exitval)
{
fprintf(stderr, "Usage: %s [options] [host [port]]\n", av0);
fprintf(stderr, " -h Print this message\n");
fprintf(stderr, " -p port Use this port as default instead of port %d.\n",
(int)CC_XSTREAM_DEFAULT_PORT);
fprintf(stderr, " -D Execute only the server discovery.\n");
fprintf(stderr, " -DD Execute the server discovery and connect to\n");
fprintf(stderr, " the first server that responds.\n");
exit(exitval);
}
int main(int argc, char **argv)
{
int port, rv, c, discover_only;
char *host;
CcBufferRec buf[0];
char *ln = NULL;
CcXstreamServerConnection s;
if (strchr(argv[0], '/'))
av0 = strrchr(argv[0], '/') + 1;
else
av0 = argv[0];
cc_buffer_init(buf);
rv = 0;
discover_only = 0;
port = CC_XSTREAM_DEFAULT_PORT;
while ((c = getopt(argc, argv, "hdDp:")) != EOF)
switch(c)
{
case 'h':
usage(0);
case 'd':
cc_debug_set_level(cc_debug_level() + 1);
break;
case 'p':
port = atoi(optarg);
if ((port < 1) || (port > 65535))
{
fprintf(stderr, "%s: Illegal port number.\n", av0);
usage(-1);
}
break;
case 'D':
discover_only++;
break;
default:
fprintf(stderr, "%s: Bad command line option -%c.\n", av0, optopt);
usage(-1);
}
argc -= optind;
argv += optind;
if (discover_only == 1)
{
discover();
exit(0);
/*NOTREACHED*/
}
else if (discover_only > 1)
{
if (argc > 0)
{
fprintf(stderr, "%s: Explicit address not allowed with server discovery.\n", av0);
usage(-1);
}
else
{
char *a = NULL, *p = NULL;
discover();
if ((discovered_server == NULL) || (discovered_port == 0))
{
fprintf(stderr, "%s: Server discovery failed.\n", av0);
exit(1);
}
host = discovered_server;
port = discovered_port;
cc_xfree(p);
if ((port < 1) || (port > 65535))
{
fprintf(stderr, "%s: Server discovery returned invalid port.\n", av0);
exit(1);
}
}
}
else
{
if (argc > 2)
{
usage(-1);
}
if (argc > 1)
{
port = atoi(argv[1]);
if ((port < 1) || (port > 65535))
{
fprintf(stderr, "%s: Illegal port number.\n", av0);
usage(-1);
}
}
if (argc > 0)
{
host = cc_xstrdup(argv[0]);
}
else
{
host = cc_xstrdup("localhost");
}
}
fprintf(stderr, "Connecting to xbmsp://%s:%d/\n", host, port);
if (cc_xstream_client_connect(host, port, &s) != CC_XSTREAM_CLIENT_OK)
{
fprintf(stderr, "%s: Can't connect to host %s.\n", av0, host);
exit(1);
}
if (cc_xstream_client_version_handshake(s) != CC_XSTREAM_CLIENT_OK)
{
fprintf(stderr, "%s: Version handshake failed.\n", av0);
rv = 2;
goto out;
}
switch (list_dir(s, 0))
{
case CC_XSTREAM_CLIENT_OK:
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "%s: Unable to list directory. May need authentication.\n", av0);
break;
default:
rv = 2;
goto out;
}
while (1) {
ln = cc_readline("> ");
if (ln == NULL)
{
fprintf(stderr, "%s: EOF. Goodbye.\n", av0);
rv = 0;
goto out;
}
else if (strcasecmp(ln, "quit") == 0)
{
fprintf(stderr, "%s: Goodbye.\n", av0);
rv = 0;
goto out;
}
else if (strcasecmp(ln, "help") == 0)
{
fprintf(stderr, "%s: Commands.\n", av0);
fprintf(stderr, " quit - Quit this program\n");
fprintf(stderr, " help - Print help\n");
fprintf(stderr, " dir - List directory\n");
fprintf(stderr, " longdir - List directory and attributes\n");
fprintf(stderr, " cd dir - Change directory (.. = up one level,\n");
fprintf(stderr, " / = root dir)\n");
fprintf(stderr, " get file - Silently get (and ignore) the file\n");
fprintf(stderr, " cat file - Get the file and print it to the screen\n");\
fprintf(stderr, " mget # file - Silently get (and ignore) the file # times\n");
fprintf(stderr, " cget #1 #2 file - Silently get (and ignore) the file #2 times\n");
fprintf(stderr, " concurrently and repeat it #1 times\n");
fprintf(stderr, " option name value - set given server option to given value\n");
fprintf(stderr, " user id password - authenticate yourself to the server with password\n");
}
else if (strcasecmp(ln, "discover") == 0)
{
discover(port, NULL, NULL, NULL);
}
else if ((strcasecmp(ln, "dir") == 0) ||
(strcasecmp(ln, "longdir") == 0))
{
int longdir;
longdir = (strcasecmp(ln, "longdir") == 0);
switch (list_dir(s, longdir))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "%s: List dir ok.\n", av0);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "%s: List dir failed.\n", av0);
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
fprintf(stderr, "%s: List dir fatally failed.\n", av0);
rv = 2;
goto out;
}
}
else if (strncasecmp(ln, "cd ", 3) == 0)
{
char *path;
unsigned long levels;
path = ln + 3;
levels = 0;
if (strcmp(path, ".") == 0)
path = "";
if (strcmp(path, "/") == 0)
levels = 0xffffffff;
else if (strcmp(path, "..") == 0)
levels = 1;
else if (strcmp(path, "...") == 0)
levels = 2;
else if (strcmp(path, "....") == 0)
levels = 3;
else if (strcmp(path, ".....") == 0)
levels = 4;
else if (strcmp(path, "......") == 0)
levels = 5;
else if (strcmp(path, ".......") == 0)
levels = 6;
else if (strcmp(path, "........") == 0)
levels = 7;
else if (strcmp(path, ".........") == 0)
levels = 8;
else if (strcmp(path, "..........") == 0)
levels = 9;
else if (strcmp(path, "...........") == 0)
levels = 10;
if (levels != 0)
{
switch (cc_xstream_client_upcwd(s, levels))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "%s: Change dir ok.\n", av0);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "%s: Change dir failed.\n", av0);
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
fprintf(stderr, "%s: Change dir fatally failed.\n", av0);
rv = 2;
goto out;
}
}
else
{
switch (cc_xstream_client_setcwd(s, path))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "%s: Change dir ok.\n", av0);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "%s: Change dir failed.\n", av0);
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
fprintf(stderr, "%s: Change dir fatally failed.\n", av0);
rv = 2;
goto out;
}
}
}
else if (strncasecmp(ln, "info ", 5) == 0)
{
char *path, *info;
path = ln + 5;
switch (cc_xstream_client_file_info(s, path, &info))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "name=\"%s\"\n", path);
fprintf(stderr, "info=\"%s\"\n", info);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "%s: File info failed.\n", av0);
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
fprintf(stderr, "%s: File info fatally failed.\n", av0);
rv = 2;
goto out;
}
}
else if (strncasecmp(ln, "option ", 7) == 0)
{
char *option, *value;
option = cc_xstrdup(ln + 7);
value = strchr(option, ' ');
if (value != NULL)
{
*value = '\0';
value++;
switch (cc_xstream_client_set_configuration_option(s, option, value))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "option=\"%s\"\n", option);
fprintf(stderr, "value=\"%s\"\n", value);
cc_xfree(option);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
cc_xfree(option);
fprintf(stderr, "%s: Set option failed.\n", av0);
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
cc_xfree(option);
fprintf(stderr, "%s: Set option fatally failed.\n", av0);
rv = 2;
goto out;
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
cc_xfree(option);
}
}
else if (strncasecmp(ln, "user ", 5) == 0)
{
char *user, *pass;
user = cc_xstrdup(ln + 5);
pass = strchr(user, ' ');
if (pass != NULL)
{
*pass = '\0';
pass++;
switch (cc_xstream_client_password_authenticate(s, user, pass))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "Password OK\n");
cc_xfree(user);
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
cc_xfree(user);
fprintf(stderr, "%s: Authentication failed.\n", av0);
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
cc_xfree(user);
fprintf(stderr, "%s: Authentication fatally failed.\n", av0);
rv = 2;
goto out;
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
cc_xfree(user);
}
}
else if ((strncasecmp(ln, "cat ", 4) == 0) ||
(strncasecmp(ln, "get ", 4) == 0))
{
int print;
print = (strncasecmp(ln, "cat ", 4) == 0);
switch (get_file(s, ln + 4, print))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "%s: %s file ok.\n", av0, (print ? "Cat" : "Get"));
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "%s: %s file failed.\n", av0, (print ? "Cat" : "Get"));
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
fprintf(stderr, "%s: %s file fatally failed.\n", av0, (print ? "Cat" : "Get"));
rv = 2;
goto out;
}
}
else if ((strncasecmp(ln, "fuzzycat ", 9) == 0) ||
(strncasecmp(ln, "fuzzyget ", 9) == 0))
{
int print;
print = (strncasecmp(ln, "fuzzycat ", 9) == 0);
switch (fuzzy_get_file(s, ln + 9, print))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, "%s: %s file ok.\n", av0, (print ? "FuzzyCat" : "FuzzyGet"));
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "%s: %s file failed.\n", av0, (print ? "FuzzyCat" : "FuzzyGet"));
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
fprintf(stderr, "%s: %s file fatally failed.\n", av0, (print ? "FuzzyCat" : "FuzzyGet"));
rv = 2;
goto out;
}
}
else if (strncasecmp(ln, "mget ", 5) == 0)
{
unsigned long x, n;
char *end;
if (isdigit(ln[5]))
{
n = strtoul(ln + 5, &end, 10);
if ((n > 0) && (*end == ' '))
{
end++;
for (x = 0; x < n; x++)
{
switch (get_file(s, end, 0))
{
case CC_XSTREAM_CLIENT_OK:
fprintf(stderr, ".");
if (x + 1 >= n)
{
fprintf(stderr, "\n%s: Get file ok.\n", av0);
}
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
if (x > 0)
fprintf(stderr, "\n");
fprintf(stderr, "%s: Get file failed.\n", av0);
x = n - 1;
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
if (x > 0)
fprintf(stderr, "\n");
fprintf(stderr, "%s: Get file fatally failed.\n", av0);
rv = 2;
goto out;
}
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
}
}
}
else if (strncasecmp(ln, "cget ", 5) == 0)
{
unsigned long x, n, m;
char *end;
if (isdigit(ln[5]))
{
n = strtoul(ln + 5, &end, 10);
if ((n > 0) && (*end == ' '))
{
end++;
if (isdigit(*end))
{
m = strtoul(end, &end, 10);
if ((m > 0) && (*end == ' '))
{
end++;
for (x = 0; x < n; x++)
{
switch (multi_get_file(s, end, m))
{
case CC_XSTREAM_CLIENT_OK:
if (x + 1 >= n)
{
fprintf(stderr, "\n%s: Get file ok.\n", av0);
}
break;
case CC_XSTREAM_CLIENT_COMMAND_FAILED:
fprintf(stderr, "\n");
fprintf(stderr, "%s: Get file failed.\n", av0);
x = n - 1;
break;
case CC_XSTREAM_CLIENT_FATAL_ERROR:
fprintf(stderr, "\n");
fprintf(stderr, "%s: Get file fatally failed.\n", av0);
rv = 2;
goto out;
}
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
}
}
else
{
fprintf(stderr, "%s: Bad command \"%s\".\n", av0, ln);
}
free(ln);
ln = NULL;
}
out:
cc_xstream_client_disconnect(s);
if (ln != NULL)
cc_xfree(ln);
exit(0);
}
/* eof (ccxtest.c) */
syntax highlighted by Code2HTML, v. 0.9.1