From 55be945c02cc4b7a67f36780d3a8eae156f49537 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Fri, 29 May 2026 17:13:47 +0200 Subject: [PATCH] nwqueue: implement change queue job entry Implement the Queue Change Job Entry endpoint for both the old NCP 17/6d and newer NCP 17/7b variants. Parse the queue job structure, look up the selected job by its job number, and update the mutable job metadata fields while preserving server-managed state such as client identity, entry time, job number, queue position, file name/handle and servicing information. Allow the job creator to update the standard user-changeable fields and allow SUPERVISOR or Q_OPERATORS members to update the operator hold flag as well. Return the documented queue/job/right/servicing completion codes for missing queues, missing jobs, insufficient rights and jobs already being serviced. This only updates NetWare queue metadata. It does not change the existing Q_UNIX_PRINT/lp backend behavior and adds no CUPS dependency. --- include/nwqueue.h | 3 +- src/nwbind.c | 6 ++- src/nwqueue.c | 96 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/include/nwqueue.h b/include/nwqueue.h index abdf26c..5799c0b 100644 --- a/include/nwqueue.h +++ b/include/nwqueue.h @@ -22,7 +22,8 @@ extern int nw_get_q_job_entry(uint32 q_id, int job_id, uint32 fhandle, extern int nw_get_queue_job_list_old(uint32 q_id, uint8 *responsedata); extern int nw_get_queue_job_list(uint32 q_id, uint32 offset, uint8 *responsedata); extern int nw_get_queue_job_file_size(uint32 q_id, int job_id); -extern int nw_change_queue_job_entry(uint32 q_id, uint8 *qjstruct); +extern int nw_change_queue_job_entry(uint32 user_id, uint32 q_id, + uint8 *qjstruct, int old_call); extern int nw_remove_job_from_queue(uint32 user_id, uint32 q_id, int job_id); diff --git a/src/nwbind.c b/src/nwbind.c index 5a5bce0..28ea7ef 100644 --- a/src/nwbind.c +++ b/src/nwbind.c @@ -1720,11 +1720,13 @@ static void handle_fxx(int gelen, int func) break; #if 1 + case 0x6D: /* Change Queue Job Entry old */ case 0x7B: /* Change Queue Job Entry */ { uint32 q_id = GET_BE32(rdata); - /* uint32 job_id = GET_BE16(rdata+4); */ - int result = nw_change_queue_job_entry(q_id, rdata+4); + int result = nw_change_queue_job_entry(act_c->object_id, + q_id, rdata+4, + ufunc==0x6D); if (result < 0) completition=(uint8)-result; } diff --git a/src/nwqueue.c b/src/nwqueue.c index 9cf9ac1..ea48c4f 100644 --- a/src/nwqueue.c +++ b/src/nwqueue.c @@ -617,11 +617,101 @@ int nw_get_queue_job_file_size(uint32 q_id, int job_id) return(result); } -int nw_change_queue_job_entry(uint32 q_id, uint8 *qjstruct) +static int is_queue_operator(NWE_QUEUE *q, uint32 user_id) { - /* TODO */ + int result; + if (user_id == 1) /* SUPERVISOR */ + return(1); + if (!q) + return(0); + result = nw_is_member_in_set(q->id, "Q_OPERATORS", user_id); + return(result == 0); +} - return(-0xfb); +static int change_queue_job_entry_old(INT_QUEUE_JOB *qj, QUEUE_JOB_OLD *job, + int operator) +{ + int old_flags = qj->job_control_flags; + int new_flags = (old_flags & 0x20) | (job->job_control_flags & 0x58); + + if (operator) + new_flags |= (job->job_control_flags & 0x80); + else + new_flags |= (old_flags & 0x80); + + qj->target_id = GET_BE32(job->target_id); + qj->execute_time = get_time_field(job->target_execute_time); + qj->job_typ = GET_BE16(job->job_typ); + qj->job_control_flags = new_flags; + memcpy(qj->job_description, job->job_description, + sizeof(qj->job_description)); + memcpy(qj->client_area, job->client_area, sizeof(qj->client_area)); + + return(old_flags); +} + +static int change_queue_job_entry(INT_QUEUE_JOB *qj, QUEUE_JOB *job, + int operator) +{ + int old_flags = qj->job_control_flags; + int request_flags = GET_16(job->job_control_flags); + int new_flags = (old_flags & 0x20) | (request_flags & 0x58); + + if (operator) + new_flags |= (request_flags & 0x80); + else + new_flags |= (old_flags & 0x80); + + qj->target_id = GET_BE32(job->target_id); + qj->execute_time = get_time_field(job->target_execute_time); + qj->job_typ = GET_BE16(job->job_typ); + qj->job_control_flags = new_flags; + memcpy(qj->job_description, job->job_description, + sizeof(qj->job_description)); + memcpy(qj->client_area, job->client_area, sizeof(qj->client_area)); + + return(old_flags); +} + +int nw_change_queue_job_entry(uint32 user_id, uint32 q_id, + uint8 *qjstruct, int old_call) +{ + int result=-0xd1; /* no queue */ + NWE_QUEUE *q=find_queue(q_id); + + if (q) { + int job_id = old_call + ? GET_BE16(((QUEUE_JOB_OLD*)qjstruct)->job_id) + : GET_BE16(((QUEUE_JOB*)qjstruct)->job_id); + INT_QUEUE_JOB *qj=find_queue_job(q, job_id); + + result=-0xd5; /* no queue job */ + if (qj) { + int operator = is_queue_operator(q, user_id); + int creator = (user_id == qj->client_id); + + result=-0xd6; /* no job right */ + if (operator || creator) { + int old_flags; + + if (qj->server_station || qj->server_id) + return(-0xd7); /* queue servicing */ + + old_flags = old_call + ? change_queue_job_entry_old(qj, (QUEUE_JOB_OLD*)qjstruct, + operator) + : change_queue_job_entry(qj, (QUEUE_JOB*)qjstruct, + operator); + q->changed++; + XDPRINTF((2, 0, + "Change Queue Job Entry q=0x%x, job=%d, user=0x%x, flags 0x%x->0x%x", + q_id, job_id, user_id, old_flags, qj->job_control_flags)); + result=0; + } + } + } + + return(result); } static int remove_queue_job_file(NWE_QUEUE *q, INT_QUEUE_JOB *qj)