/* -*- 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 #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) */