acf4c0d19afc26cb0fd16b8241cf6d537aac42b2
[mat/samba.git] / source3 / printing / printspoolss.c
1 /*
2    Unix SMB/CIFS implementation.
3    Printing routines that bridge to spoolss
4    Copyright (C) Simo Sorce 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "printing.h"
22 #include "rpc_client/rpc_client.h"
23 #include "../librpc/gen_ndr/ndr_spoolss_c.h"
24 #include "rpc_server/rpc_ncacn_np.h"
25 #include "smbd/globals.h"
26 #include "../libcli/security/security.h"
27
28 struct print_file_data {
29         char *svcname;
30         char *docname;
31         char *filename;
32         struct policy_handle handle;
33         uint32_t jobid;
34         uint16 rap_jobid;
35 };
36
37 uint16_t print_spool_rap_jobid(struct print_file_data *print_file)
38 {
39         if (print_file == NULL) {
40                 return 0;
41         }
42
43         return print_file->rap_jobid;
44 }
45
46 void print_spool_terminate(struct connection_struct *conn,
47                            struct print_file_data *print_file);
48
49 /***************************************************************************
50  * Open a Document over spoolss
51  ***************************************************************************/
52
53 #define DOCNAME_DEFAULT "Remote Downlevel Document"
54 #ifndef PRINT_SPOOL_PREFIX
55 #define PRINT_SPOOL_PREFIX "smbprn."
56 #endif
57
58 NTSTATUS print_spool_open(files_struct *fsp,
59                           const char *fname,
60                           uint64_t current_vuid)
61 {
62         NTSTATUS status;
63         TALLOC_CTX *tmp_ctx;
64         struct print_file_data *pf;
65         struct dcerpc_binding_handle *b = NULL;
66         struct spoolss_DevmodeContainer devmode_ctr;
67         struct spoolss_DocumentInfoCtr info_ctr;
68         struct spoolss_DocumentInfo1 *info1;
69         int fd = -1;
70         WERROR werr;
71
72         tmp_ctx = talloc_new(fsp);
73         if (!tmp_ctx) {
74                 return NT_STATUS_NO_MEMORY;
75         }
76
77         pf = talloc_zero(fsp, struct print_file_data);
78         if (!pf) {
79                 status = NT_STATUS_NO_MEMORY;
80                 goto done;
81         }
82         pf->svcname = lp_servicename(pf, SNUM(fsp->conn));
83
84         /* the document name is derived from the file name.
85          * "Remote Downlevel Document" is added in front to
86          * mimic what windows does in this case */
87         pf->docname = talloc_strdup(pf, DOCNAME_DEFAULT);
88         if (!pf->docname) {
89                 status = NT_STATUS_NO_MEMORY;
90                 goto done;
91         }
92         if (fname) {
93                 const char *p = strrchr(fname, '/');
94                 if (!p) {
95                         p = fname;
96                 }
97                 pf->docname = talloc_asprintf_append(pf->docname, " %s", p);
98                 if (!pf->docname) {
99                         status = NT_STATUS_NO_MEMORY;
100                         goto done;
101                 }
102         }
103
104         /*
105          * Ok, now we have to open an actual file.
106          * Here is the reason:
107          * We want to write the spool job to this file in
108          * smbd for scalability reason (and also because
109          * apparently window printer drivers can seek when
110          * spooling to a file).
111          * So we first create a file, and then we pass it
112          * to spoolss in output_file so it can monitor and
113          * take over once we call EndDocPrinter().
114          * Of course we will not start writing until
115          * StartDocPrinter() actually gives the ok.
116          * smbd spooler files do not include a print jobid
117          * path component, as the jobid is only known after
118          * calling StartDocPrinter().
119          */
120
121         pf->filename = talloc_asprintf(pf, "%s/%sXXXXXX",
122                                         lp_pathname(talloc_tos(),
123                                                     SNUM(fsp->conn)),
124                                         PRINT_SPOOL_PREFIX);
125         if (!pf->filename) {
126                 status = NT_STATUS_NO_MEMORY;
127                 goto done;
128         }
129         errno = 0;
130         fd = mkstemp(pf->filename);
131         if (fd == -1) {
132                 if (errno == EACCES) {
133                         /* Common setup error, force a report. */
134                         DEBUG(0, ("Insufficient permissions "
135                                   "to open spool file %s.\n",
136                                   pf->filename));
137                 } else {
138                         /* Normal case, report at level 3 and above. */
139                         DEBUG(3, ("can't open spool file %s,\n",
140                                   pf->filename));
141                         DEBUGADD(3, ("errno = %d (%s).\n",
142                                      errno, strerror(errno)));
143                 }
144                 status = map_nt_error_from_unix(errno);
145                 goto done;
146         }
147
148         /* now open a document over spoolss so that it does
149          * all printer verification, and eventually assigns
150          * a job id */
151
152         status = rpc_pipe_open_interface(fsp->conn,
153                                          &ndr_table_spoolss.syntax_id,
154                                          fsp->conn->session_info,
155                                          fsp->conn->sconn->remote_address,
156                                          fsp->conn->sconn->msg_ctx,
157                                          &fsp->conn->spoolss_pipe);
158         if (!NT_STATUS_IS_OK(status)) {
159                 goto done;
160         }
161         b = fsp->conn->spoolss_pipe->binding_handle;
162
163         ZERO_STRUCT(devmode_ctr);
164
165         status = dcerpc_spoolss_OpenPrinter(b, pf, pf->svcname,
166                                             "RAW", devmode_ctr,
167                                             PRINTER_ACCESS_USE,
168                                             &pf->handle, &werr);
169         if (!NT_STATUS_IS_OK(status)) {
170                 goto done;
171         }
172         if (!W_ERROR_IS_OK(werr)) {
173                 status = werror_to_ntstatus(werr);
174                 goto done;
175         }
176
177         info1 = talloc(tmp_ctx, struct spoolss_DocumentInfo1);
178         if (info1 == NULL) {
179                 status = NT_STATUS_NO_MEMORY;
180                 goto done;
181         }
182         info1->document_name = pf->docname;
183         info1->output_file = pf->filename;
184         info1->datatype = "RAW";
185
186         info_ctr.level = 1;
187         info_ctr.info.info1 = info1;
188
189         status = dcerpc_spoolss_StartDocPrinter(b, tmp_ctx,
190                                                 &pf->handle,
191                                                 &info_ctr,
192                                                 &pf->jobid,
193                                                 &werr);
194         if (!NT_STATUS_IS_OK(status)) {
195                 goto done;
196         }
197         if (!W_ERROR_IS_OK(werr)) {
198                 status = werror_to_ntstatus(werr);
199                 goto done;
200         }
201
202         /* Convert to RAP id. */
203         pf->rap_jobid = pjobid_to_rap(pf->svcname, pf->jobid);
204         if (pf->rap_jobid == 0) {
205                 /* No errno around here */
206                 status = NT_STATUS_ACCESS_DENIED;
207                 goto done;
208         }
209
210         /* setup a full fsp */
211         status = create_synthetic_smb_fname(fsp, pf->filename, NULL,
212                                             NULL, &fsp->fsp_name);
213         if (!NT_STATUS_IS_OK(status)) {
214                 goto done;
215         }
216
217         if (sys_fstat(fd, &fsp->fsp_name->st, false) != 0) {
218                 status = map_nt_error_from_unix(errno);
219                 goto done;
220         }
221
222         fsp->file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st);
223         fsp->fh->fd = fd;
224
225         fsp->vuid = current_vuid;
226         fsp->can_lock = false;
227         fsp->can_read = false;
228         fsp->access_mask = FILE_GENERIC_WRITE;
229         fsp->can_write = true;
230         fsp->modified = false;
231         fsp->oplock_type = NO_OPLOCK;
232         fsp->sent_oplock_break = NO_BREAK_SENT;
233         fsp->is_directory = false;
234
235         fsp->print_file = pf;
236
237         status = NT_STATUS_OK;
238 done:
239         if (!NT_STATUS_IS_OK(status)) {
240                 if (fd != -1) {
241                         close(fd);
242                         if (fsp->print_file) {
243                                 unlink(fsp->print_file->filename);
244                         }
245                 }
246                 /* We need to delete the job from spoolss too */
247                 if (pf->jobid) {
248                         print_spool_terminate(fsp->conn, pf);
249                 }
250         }
251         talloc_free(tmp_ctx);
252         return status;
253 }
254
255 int print_spool_write(files_struct *fsp,
256                       const char *data, uint32_t size,
257                       off_t offset, uint32_t *written)
258 {
259         SMB_STRUCT_STAT st;
260         ssize_t n;
261         int ret;
262
263         *written = 0;
264
265         /* first of all stat file to find out if it is still there.
266          * spoolss may have deleted it to signal someone has killed
267          * the job through it's interface */
268
269         if (sys_fstat(fsp->fh->fd, &st, false) != 0) {
270                 ret = errno;
271                 DEBUG(3, ("printfile_offset: sys_fstat failed on %s (%s)\n",
272                           fsp_str_dbg(fsp), strerror(ret)));
273                 return ret;
274         }
275
276         /* check if the file is unlinked, this will signal spoolss has
277          * killed it, just return an error and close the file */
278         if (st.st_ex_nlink == 0) {
279                 close(fsp->fh->fd);
280                 return EBADF;
281         }
282
283         /* When print files go beyond 4GB, the 32-bit offset sent in
284          * old SMBwrite calls is relative to the current 4GB chunk
285          * we're writing to.
286          *    Discovered by Sebastian Kloska <oncaphillis@snafu.de>.
287          */
288         if (offset < 0xffffffff00000000LL) {
289                 offset = (st.st_ex_size & 0xffffffff00000000LL) + offset;
290         }
291
292         n = write_data_at_offset(fsp->fh->fd, data, size, offset);
293         if (n == -1) {
294                 ret = errno;
295                 print_spool_terminate(fsp->conn, fsp->print_file);
296         } else {
297                 *written = n;
298                 ret = 0;
299         }
300
301         return ret;
302 }
303
304 void print_spool_end(files_struct *fsp, enum file_close_type close_type)
305 {
306         NTSTATUS status;
307         WERROR werr;
308         struct dcerpc_binding_handle *b = NULL;
309
310         b = fsp->conn->spoolss_pipe->binding_handle;
311
312         switch (close_type) {
313         case NORMAL_CLOSE:
314         case SHUTDOWN_CLOSE:
315                 /* this also automatically calls spoolss_EndDocPrinter */
316                 status = dcerpc_spoolss_ClosePrinter(b, fsp->print_file,
317                                                 &fsp->print_file->handle,
318                                                 &werr);
319                 if (!NT_STATUS_IS_OK(status) ||
320                     !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) {
321                         DEBUG(3, ("Failed to close printer %s [%s]\n",
322                               fsp->print_file->svcname, nt_errstr(status)));
323                 }
324                 break;
325         case ERROR_CLOSE:
326                 print_spool_terminate(fsp->conn, fsp->print_file);
327                 break;
328         }
329 }
330
331
332 void print_spool_terminate(struct connection_struct *conn,
333                            struct print_file_data *print_file)
334 {
335         NTSTATUS status;
336         WERROR werr;
337         struct dcerpc_binding_handle *b = NULL;
338
339         rap_jobid_delete(print_file->svcname, print_file->jobid);
340
341         status = rpc_pipe_open_interface(conn,
342                                          &ndr_table_spoolss.syntax_id,
343                                          conn->session_info,
344                                          conn->sconn->remote_address,
345                                          conn->sconn->msg_ctx,
346                                          &conn->spoolss_pipe);
347         if (!NT_STATUS_IS_OK(status)) {
348                 DEBUG(0, ("print_spool_terminate: "
349                           "Failed to get spoolss pipe [%s]\n",
350                           nt_errstr(status)));
351                 return;
352         }
353         b = conn->spoolss_pipe->binding_handle;
354
355         status = dcerpc_spoolss_SetJob(b, print_file,
356                                         &print_file->handle,
357                                         print_file->jobid,
358                                         NULL, SPOOLSS_JOB_CONTROL_DELETE,
359                                         &werr);
360         if (!NT_STATUS_IS_OK(status) ||
361             !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) {
362                 DEBUG(3, ("Failed to delete job %d [%s]\n",
363                           print_file->jobid, nt_errstr(status)));
364                 return;
365         }
366         status = dcerpc_spoolss_ClosePrinter(b, print_file,
367                                              &print_file->handle,
368                                              &werr);
369         if (!NT_STATUS_IS_OK(status) ||
370             !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) {
371                 DEBUG(3, ("Failed to close printer %s [%s]\n",
372                           print_file->svcname, nt_errstr(status)));
373                 return;
374         }
375 }