#include "includes.h"
#define MAX_RETRY_CONNECT 3
extern BOOL in_client;
static int get_exit_code(struct cli_state *cli, NTSTATUS nt_status);
static void list_devices(void);
static struct cli_state *smb_complete_connection(const char *, const char *,int , const char *, const char *, const char *, const char *, int, int *need_auth);
static struct cli_state *smb_connect(const char *, const char *, int, const char *, const char *, const char *, const char *, int *need_auth);
static int smb_print(struct cli_state *, char *, FILE *);
static char * uri_unescape_alloc(const char *);
int
main(int argc,
char *argv[])
{
int i;
int copies;
int port;
char uri[1024],
*sep,
*tmp, *tmp2,
*password;
char *username,
*server,
*printer;
const char *workgroup;
FILE *fp;
int status=0;
struct cli_state *cli;
char null_str[1];
int tries = 0;
const char *dev_uri;
int need_auth;
null_str[0] = '\0';
if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) {
argv++;
argc--;
}
if (argc == 1)
{
list_devices();
return (0);
}
if (argc < 6 || argc > 7)
{
fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n",
argv[0]);
fputs(" The DEVICE_URI environment variable can also contain the\n", stderr);
fputs(" destination printer:\n", stderr);
fputs("\n", stderr);
fputs(" smb://[username:password@][workgroup/]server[:port]/printer\n", stderr);
return (1);
}
if (argc == 6)
{
fp = stdin;
copies = 1;
}
else if ((fp = fopen(argv[6], "rb")) == NULL)
{
perror("ERROR: Unable to open print file");
return (1);
}
else
copies = atoi(argv[4]);
dev_uri = getenv("DEVICE_URI");
if (dev_uri)
strncpy(uri, dev_uri, sizeof(uri) - 1);
else if (strncmp(argv[0], "smb://", 6) == 0)
strncpy(uri, argv[0], sizeof(uri) - 1);
else
{
fputs("ERROR: No device URI found in DEVICE_URI environment variable or argv[0] !\n", stderr);
return (1);
}
uri[sizeof(uri) - 1] = '\0';
if ((sep = strrchr_m(uri, '@')) != NULL)
{
tmp = uri + 6;
*sep++ = '\0';
server = sep;
if ((tmp2 = strchr_m(tmp, ':')) != NULL) {
*tmp2++ = '\0';
password = uri_unescape_alloc(tmp2);
} else {
password = null_str;
}
username = uri_unescape_alloc(tmp);
}
else
{
if ((username = getenv("AUTH_USERNAME")) == NULL)
username = null_str;
if ((password = getenv("AUTH_PASSWORD")) == NULL)
password = null_str;
server = uri + 6;
}
tmp = server;
if ((sep = strchr_m(tmp, '/')) == NULL)
{
fputs("ERROR: Bad URI - need printer name!\n", stderr);
return (1);
}
*sep++ = '\0';
tmp2 = sep;
if ((sep = strchr_m(tmp2, '/')) != NULL)
{
*sep++ = '\0';
workgroup = uri_unescape_alloc(tmp);
server = uri_unescape_alloc(tmp2);
printer = uri_unescape_alloc(sep);
}
else {
workgroup = NULL;
server = uri_unescape_alloc(tmp);
printer = uri_unescape_alloc(tmp2);
}
if ((sep = strrchr_m(server, ':')) != NULL)
{
*sep++ = '\0';
port=atoi(sep);
}
else
port=0;
setup_logging("smbspool", True);
in_client = True;
load_case_tables();
if (!lp_load(dyn_CONFIGFILE, True, False, False, True))
{
fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
return (1);
}
if (workgroup == NULL)
workgroup = lp_workgroup();
load_interfaces();
do
{
if ((cli = smb_connect(workgroup, server, port, printer, username, password, argv[2], &need_auth)) == NULL)
{
if (need_auth)
exit(2);
else if (getenv("CLASS") == NULL)
{
fprintf(stderr, "ERROR: Unable to connect to CIFS host, will retry in 60 seconds...\n");
sleep(60);
tries++;
}
else
{
fprintf(stderr, "ERROR: Unable to connect to CIFS host, trying next printer...\n");
return (1);
}
}
}
while ((cli == NULL) && (tries < MAX_RETRY_CONNECT));
if (cli == NULL) {
fprintf(stderr, "ERROR: Unable to connect to CIFS host after (tried %d times)\n", tries);
return (1);
}
if (argc < 7)
CatchSignal(SIGTERM, SIG_IGN);
for (i = 0; i < copies; i ++)
if ((status = smb_print(cli, argv[3] , fp)) != 0)
break;
cli_shutdown(cli);
return (status);
}
static int
get_exit_code(struct cli_state *cli,
NTSTATUS nt_status)
{
int i;
static const NTSTATUS auth_errors[] =
{
NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_VIOLATION,
NT_STATUS_SHARING_VIOLATION, NT_STATUS_PRIVILEGE_NOT_HELD,
NT_STATUS_INVALID_ACCOUNT_NAME, NT_STATUS_NO_SUCH_USER,
NT_STATUS_WRONG_PASSWORD, NT_STATUS_LOGON_FAILURE,
NT_STATUS_ACCOUNT_RESTRICTION, NT_STATUS_INVALID_LOGON_HOURS,
NT_STATUS_PASSWORD_EXPIRED, NT_STATUS_ACCOUNT_DISABLED
};
fprintf(stderr, "DEBUG: get_exit_code(cli=%p, nt_status=%x)\n",
cli, NT_STATUS_V(nt_status));
for (i = 0; i < (int)(sizeof(auth_errors) / sizeof(auth_errors[0])); i ++)
if (NT_STATUS_V(nt_status) == NT_STATUS_V(auth_errors[i]))
{
if (cli)
{
if (cli->use_kerberos && cli->got_kerberos_mechanism)
fputs("ATTR: auth-info-required=negotiate\n", stderr);
else
fputs("ATTR: auth-info-required=username,password\n", stderr);
}
return (2);
}
return (1);
}
static void
list_devices(void)
{
puts("network smb \"Unknown\" \"Windows Printer via SAMBA\"");
}
static struct cli_state
*smb_complete_connection(const char *myname,
const char *server,
int port,
const char *username,
const char *password,
const char *workgroup,
const char *share,
int flags,
int *need_auth)
{
struct cli_state *cli;
NTSTATUS nt_status;
*need_auth = 0;
nt_status = cli_start_connection( &cli, myname, server, NULL, port,
Undefined, flags, NULL);
if (!NT_STATUS_IS_OK(nt_status))
{
fprintf(stderr,"ERROR: Connection failed: %s\n", nt_errstr(nt_status));
return NULL;
}
if (!password) {
*need_auth = 1;
return NULL;
}
nt_status = cli_session_setup(cli, username,
password, strlen(password)+1,
password, strlen(password)+1,
workgroup);
if (!NT_STATUS_IS_OK(nt_status))
{
fprintf(stderr,"ERROR: Session setup failed: %s\n", nt_errstr(nt_status));
if (get_exit_code(cli, nt_status) == 2)
*need_auth = 1;
cli_shutdown(cli);
return NULL;
}
if (!cli_send_tconX(cli, share, "?????", password, strlen(password)+1))
{
fprintf(stderr, "ERROR: Tree connect failed (%s)\n", cli_errstr(cli));
if (get_exit_code(cli, cli_nt_error(cli)) == 2)
*need_auth = 1;
cli_shutdown(cli);
return NULL;
}
return cli;
}
static struct cli_state *
smb_connect(const char *workgroup,
const char *server,
const int port,
const char *share,
const char *username,
const char *password,
const char *jobusername,
int *need_auth)
{
struct cli_state *cli;
pstring myname;
struct passwd *pwd;
get_myname(myname);
if (username && *username && !getenv("KRB5CCNAME"))
{
cli = smb_complete_connection(myname, server, port, username,
password, workgroup, share, 0, need_auth);
if (cli)
{
fputs("DEBUG: Connected with username/password...\n", stderr);
return (cli);
}
}
cli = smb_complete_connection(myname, server, port, jobusername, "",
workgroup, share,
CLI_FULL_CONNECTION_USE_KERBEROS, need_auth);
if (cli)
{
fputs("DEBUG: Connected using Kerberos...\n", stderr);
return (cli);
}
pwd = getpwuid(geteuid());
if (pwd == NULL) {
return NULL;
}
cli = smb_complete_connection(myname, server, port, pwd->pw_name, "",
workgroup, share, 0, need_auth);
if (cli)
{
fputs("DEBUG: Connected with NTLMSSP...\n", stderr);
return (cli);
}
cli = smb_complete_connection(myname, server, port, "", "",
workgroup, share, 0, need_auth);
return (cli);
}
static int
smb_print(struct cli_state *cli,
char *title,
FILE *fp)
{
int fnum;
int nbytes,
tbytes;
char buffer[8192],
*ptr;
for (ptr = title; *ptr; ptr ++)
if (!isalnum((int)*ptr) && !isspace((int)*ptr))
*ptr = '_';
if ((fnum = cli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE)) == -1)
{
fprintf(stderr, "ERROR: %s opening remote spool %s\n",
cli_errstr(cli), title);
return (get_exit_code(cli, cli_nt_error(cli)));
}
if (fp != stdin)
rewind(fp);
tbytes = 0;
while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
{
if (cli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes)
{
int status = get_exit_code(cli, cli_nt_error(cli));
fprintf(stderr, "ERROR: Error writing spool: %s\n", cli_errstr(cli));
fprintf(stderr, "DEBUG: Returning status %d...\n", status);
cli_close(cli, fnum);
return (status);
}
tbytes += nbytes;
}
if (!cli_close(cli, fnum))
{
fprintf(stderr, "ERROR: %s closing remote spool %s\n",
cli_errstr(cli), title);
return (get_exit_code(cli, cli_nt_error(cli)));
}
else
return (0);
}
static char *uri_unescape_alloc(const char *uritok)
{
char *ret;
ret = (char *)SMB_STRDUP(uritok);
if (!ret) return NULL;
rfc1738_unescape(ret);
return ret;
}