Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/scheduler/cups-lpd.c
1090 views
1
/*
2
* Line Printer Daemon interface for CUPS.
3
*
4
* Copyright © 2021-2022 by OpenPrinting.
5
* Copyright © 2007-2016 by Apple Inc.
6
* Copyright © 1997-2006 by Easy Software Products, all rights reserved.
7
*
8
* Licensed under Apache License v2.0. See the file "LICENSE" for more
9
* information.
10
*/
11
12
/*
13
* Include necessary headers...
14
*/
15
16
#define _CUPS_NO_DEPRECATED
17
#include <cups/cups-private.h>
18
#include <syslog.h>
19
#include <unistd.h>
20
#include <fcntl.h>
21
#include <sys/types.h>
22
#include <sys/socket.h>
23
#include <netinet/in.h>
24
#include <netdb.h>
25
26
#ifdef HAVE_INTTYPES_H
27
# include <inttypes.h>
28
#endif /* HAVE_INTTYPES_H */
29
#ifdef __APPLE__
30
# include <xpc/xpc.h>
31
#endif /* __APPLE__ */
32
33
34
/*
35
* LPD "mini-daemon" for CUPS. This program must be used in conjunction
36
* with inetd or another similar program that monitors ports and starts
37
* daemons for each client connection. A typical configuration is:
38
*
39
* printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
40
*
41
* This daemon implements most of RFC 1179 (the unofficial LPD specification)
42
* except for:
43
*
44
* - This daemon does not check to make sure that the source port is
45
* between 721 and 731, since it isn't necessary for proper
46
* functioning and port-based security is no security at all!
47
*
48
* - The "Print any waiting jobs" command is a no-op.
49
*
50
* The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
51
* currently match the Solaris LPD mini-daemon.
52
*/
53
54
/*
55
* Prototypes...
56
*/
57
58
static int create_job(http_t *http, const char *dest, const char *title, const char *user, int num_options, cups_option_t *options);
59
static int get_printer(http_t *http, const char *name, char *dest,
60
size_t destsize, cups_option_t **options,
61
int *accepting, int *shared, ipp_pstate_t *state);
62
static int print_file(http_t *http, int id, const char *filename,
63
const char *docname, const char *user,
64
const char *format, int last);
65
static int recv_print_job(const char *name, int num_defaults,
66
cups_option_t *defaults);
67
static int remove_jobs(const char *name, const char *agent,
68
const char *list);
69
static int send_state(const char *name, const char *list,
70
int longstatus);
71
static char *smart_gets(char *s, int len, FILE *fp);
72
static void smart_strlcpy(char *dst, const char *src, size_t dstsize);
73
74
75
/*
76
* 'main()' - Process an incoming LPD request...
77
*/
78
79
int /* O - Exit status */
80
main(int argc, /* I - Number of command-line arguments */
81
char *argv[]) /* I - Command-line arguments */
82
{
83
int i; /* Looping var */
84
int num_defaults; /* Number of default options */
85
cups_option_t *defaults; /* Default options */
86
char line[256], /* Command string */
87
command, /* Command code */
88
*dest, /* Pointer to destination */
89
*list, /* Pointer to list */
90
*agent, /* Pointer to user */
91
status; /* Status for client */
92
socklen_t hostlen; /* Size of client address */
93
http_addr_t hostaddr; /* Address of client */
94
char hostname[256], /* Name of client */
95
hostip[256], /* IP address */
96
*hostfamily; /* Address family */
97
int hostlookups; /* Do hostname lookups? */
98
99
100
#ifdef __APPLE__
101
xpc_transaction_begin();
102
#endif /* __APPLE__ */
103
104
/*
105
* Don't buffer the output...
106
*/
107
108
setbuf(stdout, NULL);
109
110
/*
111
* Log things using the "cups-lpd" name...
112
*/
113
114
openlog("cups-lpd", LOG_PID, LOG_LPR);
115
116
/*
117
* Scan the command-line for options...
118
*/
119
120
num_defaults = 0;
121
defaults = NULL;
122
hostlookups = 1;
123
124
for (i = 1; i < argc; i ++)
125
if (argv[i][0] == '-')
126
{
127
switch (argv[i][1])
128
{
129
case 'h' : /* -h hostname[:port] */
130
if (argv[i][2])
131
cupsSetServer(argv[i] + 2);
132
else
133
{
134
i ++;
135
if (i < argc)
136
cupsSetServer(argv[i]);
137
else
138
syslog(LOG_WARNING, "Expected hostname string after -h option!");
139
}
140
break;
141
142
case 'o' : /* Option */
143
if (argv[i][2])
144
num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
145
&defaults);
146
else
147
{
148
i ++;
149
if (i < argc)
150
num_defaults = cupsParseOptions(argv[i], num_defaults,
151
&defaults);
152
else
153
syslog(LOG_WARNING, "Expected option string after -o option!");
154
}
155
break;
156
157
case 'n' : /* Don't do hostname lookups */
158
hostlookups = 0;
159
break;
160
161
default :
162
syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
163
break;
164
}
165
}
166
else
167
syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!",
168
argv[i]);
169
170
/*
171
* Get the address of the client...
172
*/
173
174
hostlen = sizeof(hostaddr);
175
176
if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
177
{
178
syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
179
strlcpy(hostname, "unknown", sizeof(hostname));
180
}
181
else
182
{
183
httpAddrString(&hostaddr, hostip, sizeof(hostip));
184
185
if (hostlookups)
186
httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
187
else
188
strlcpy(hostname, hostip, sizeof(hostname));
189
190
#ifdef AF_INET6
191
if (hostaddr.addr.sa_family == AF_INET6)
192
hostfamily = "IPv6";
193
else
194
#endif /* AF_INET6 */
195
hostfamily = "IPv4";
196
197
syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily,
198
hostip);
199
}
200
201
num_defaults = cupsAddOption("job-originating-host-name", hostname,
202
num_defaults, &defaults);
203
204
/*
205
* RFC1179 specifies that only 1 daemon command can be received for
206
* every connection.
207
*/
208
209
if (smart_gets(line, sizeof(line), stdin) == NULL)
210
{
211
/*
212
* Unable to get command from client! Send an error status and return.
213
*/
214
215
syslog(LOG_ERR, "Unable to get command line from client!");
216
putchar(1);
217
218
#ifdef __APPLE__
219
xpc_transaction_end();
220
#endif /* __APPLE__ */
221
222
return (1);
223
}
224
225
/*
226
* The first byte is the command byte. After that will be the queue name,
227
* resource list, and/or user name.
228
*/
229
230
if ((command = line[0]) == '\0')
231
dest = line;
232
else
233
dest = line + 1;
234
235
if (command == 0x02)
236
list = NULL;
237
else
238
{
239
for (list = dest; *list && !isspace(*list & 255); list ++);
240
241
while (isspace(*list & 255))
242
*list++ = '\0';
243
}
244
245
/*
246
* Do the command...
247
*/
248
249
switch (command)
250
{
251
default : /* Unknown command */
252
syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
253
syslog(LOG_ERR, "Command line = %s", line + 1);
254
putchar(1);
255
256
status = 1;
257
break;
258
259
case 0x01 : /* Print any waiting jobs */
260
syslog(LOG_INFO, "Print waiting jobs (no-op)");
261
putchar(0);
262
263
status = 0;
264
break;
265
266
case 0x02 : /* Receive a printer job */
267
syslog(LOG_INFO, "Receive print job for %s", dest);
268
/* recv_print_job() sends initial status byte */
269
270
status = (char)recv_print_job(dest, num_defaults, defaults);
271
break;
272
273
case 0x03 : /* Send queue state (short) */
274
syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
275
/* no status byte for this command */
276
277
status = (char)send_state(dest, list, 0);
278
break;
279
280
case 0x04 : /* Send queue state (long) */
281
syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
282
/* no status byte for this command */
283
284
status = (char)send_state(dest, list, 1);
285
break;
286
287
case 0x05 : /* Remove jobs */
288
/*
289
* Grab the agent and skip to the list of users and/or jobs.
290
*/
291
292
agent = list;
293
294
for (; *list && !isspace(*list & 255); list ++);
295
while (isspace(*list & 255))
296
*list++ = '\0';
297
298
syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
299
300
status = (char)remove_jobs(dest, agent, list);
301
302
putchar(status);
303
break;
304
}
305
306
syslog(LOG_INFO, "Closing connection");
307
closelog();
308
309
#ifdef __APPLE__
310
xpc_transaction_end();
311
#endif /* __APPLE__ */
312
313
return (status);
314
}
315
316
317
/*
318
* 'create_job()' - Create a new print job.
319
*/
320
321
static int /* O - Job ID or -1 on error */
322
create_job(http_t *http, /* I - HTTP connection */
323
const char *dest, /* I - Destination name */
324
const char *title, /* I - job-name */
325
const char *user, /* I - requesting-user-name */
326
int num_options, /* I - Number of options for job */
327
cups_option_t *options) /* I - Options for job */
328
{
329
ipp_t *request; /* IPP request */
330
ipp_t *response; /* IPP response */
331
ipp_attribute_t *attr; /* IPP attribute */
332
char uri[HTTP_MAX_URI]; /* Printer URI */
333
int id; /* Job ID */
334
335
336
/*
337
* Setup the Create-Job request...
338
*/
339
340
request = ippNewRequest(IPP_OP_CREATE_JOB);
341
342
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
343
"localhost", 0, "/printers/%s", dest);
344
345
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
346
NULL, uri);
347
348
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
349
"requesting-user-name", NULL, user);
350
351
if (title[0])
352
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
353
NULL, title);
354
355
cupsEncodeOptions(request, num_options, options);
356
357
/*
358
* Do the request...
359
*/
360
361
snprintf(uri, sizeof(uri), "/printers/%s", dest);
362
363
response = cupsDoRequest(http, request, uri);
364
365
if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
366
{
367
syslog(LOG_ERR, "Unable to create job - %s", cupsLastErrorString());
368
369
ippDelete(response);
370
371
return (-1);
372
}
373
374
/*
375
* Get the job-id value from the response and return it...
376
*/
377
378
if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
379
{
380
id = -1;
381
382
syslog(LOG_ERR, "No job-id attribute found in response from server!");
383
}
384
else
385
{
386
id = attr->values[0].integer;
387
388
syslog(LOG_INFO, "Print file - job ID = %d", id);
389
}
390
391
ippDelete(response);
392
393
return (id);
394
}
395
396
397
/*
398
* 'get_printer()' - Get the named printer and its options.
399
*/
400
401
static int /* O - Number of options or -1 on error */
402
get_printer(http_t *http, /* I - HTTP connection */
403
const char *name, /* I - Printer name from request */
404
char *dest, /* I - Destination buffer */
405
size_t destsize, /* I - Size of destination buffer */
406
cups_option_t **options, /* O - Printer options */
407
int *accepting, /* O - printer-is-accepting-jobs value */
408
int *shared, /* O - printer-is-shared value */
409
ipp_pstate_t *state) /* O - printer-state value */
410
{
411
int num_options; /* Number of options */
412
cups_file_t *fp; /* lpoptions file */
413
char line[1024], /* Line from lpoptions file */
414
*value, /* Pointer to value on line */
415
*optptr; /* Pointer to options on line */
416
int linenum; /* Line number in file */
417
const char *cups_serverroot; /* CUPS_SERVERROOT env var */
418
ipp_t *request; /* IPP request */
419
ipp_t *response; /* IPP response */
420
ipp_attribute_t *attr; /* IPP attribute */
421
char uri[HTTP_MAX_URI]; /* Printer URI */
422
static const char * const requested[] =
423
{ /* Requested attributes */
424
"printer-info",
425
"printer-is-accepting-jobs",
426
"printer-is-shared",
427
"printer-name",
428
"printer-state"
429
};
430
431
432
/*
433
* Initialize everything...
434
*/
435
436
if (accepting)
437
*accepting = 0;
438
if (shared)
439
*shared = 0;
440
if (state)
441
*state = IPP_PSTATE_STOPPED;
442
if (options)
443
*options = NULL;
444
445
/*
446
* See if the name is a queue name optionally with an instance name.
447
*/
448
449
strlcpy(dest, name, destsize);
450
if ((value = strchr(dest, '/')) != NULL)
451
*value = '\0';
452
453
/*
454
* Setup the Get-Printer-Attributes request...
455
*/
456
457
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
458
459
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
460
"localhost", 0, "/printers/%s", dest);
461
462
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
463
NULL, uri);
464
465
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
466
"requested-attributes",
467
(int)(sizeof(requested) / sizeof(requested[0])),
468
NULL, requested);
469
470
/*
471
* Do the request...
472
*/
473
474
response = cupsDoRequest(http, request, "/");
475
476
if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
477
{
478
/*
479
* If we can't find the printer by name, look up the printer-name
480
* using the printer-info values...
481
*/
482
483
ipp_attribute_t *accepting_attr,/* printer-is-accepting-jobs */
484
*info_attr, /* printer-info */
485
*name_attr, /* printer-name */
486
*shared_attr, /* printer-is-shared */
487
*state_attr; /* printer-state */
488
489
490
ippDelete(response);
491
492
/*
493
* Setup the CUPS-Get-Printers request...
494
*/
495
496
request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS);
497
498
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
499
"requested-attributes",
500
(int)(sizeof(requested) / sizeof(requested[0])),
501
NULL, requested);
502
503
/*
504
* Do the request...
505
*/
506
507
response = cupsDoRequest(http, request, "/");
508
509
if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
510
{
511
syslog(LOG_ERR, "Unable to get list of printers - %s",
512
cupsLastErrorString());
513
514
ippDelete(response);
515
516
return (-1);
517
}
518
519
/*
520
* Scan the response for printers...
521
*/
522
523
*dest = '\0';
524
attr = response->attrs;
525
526
while (attr)
527
{
528
/*
529
* Skip to the next printer...
530
*/
531
532
while (attr && attr->group_tag != IPP_TAG_PRINTER)
533
attr = attr->next;
534
535
if (!attr)
536
break;
537
538
/*
539
* Get all of the attributes for the current printer...
540
*/
541
542
accepting_attr = NULL;
543
info_attr = NULL;
544
name_attr = NULL;
545
shared_attr = NULL;
546
state_attr = NULL;
547
548
while (attr && attr->group_tag == IPP_TAG_PRINTER)
549
{
550
if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
551
attr->value_tag == IPP_TAG_BOOLEAN)
552
accepting_attr = attr;
553
else if (!strcmp(attr->name, "printer-info") &&
554
attr->value_tag == IPP_TAG_TEXT)
555
info_attr = attr;
556
else if (!strcmp(attr->name, "printer-name") &&
557
attr->value_tag == IPP_TAG_NAME)
558
name_attr = attr;
559
else if (!strcmp(attr->name, "printer-is-shared") &&
560
attr->value_tag == IPP_TAG_BOOLEAN)
561
shared_attr = attr;
562
else if (!strcmp(attr->name, "printer-state") &&
563
attr->value_tag == IPP_TAG_ENUM)
564
state_attr = attr;
565
566
attr = attr->next;
567
}
568
569
if (info_attr && name_attr &&
570
!_cups_strcasecmp(name, info_attr->values[0].string.text))
571
{
572
/*
573
* Found a match, use this one!
574
*/
575
576
strlcpy(dest, name_attr->values[0].string.text, destsize);
577
578
if (accepting && accepting_attr)
579
*accepting = accepting_attr->values[0].boolean;
580
581
if (shared && shared_attr)
582
*shared = shared_attr->values[0].boolean;
583
584
if (state && state_attr)
585
*state = (ipp_pstate_t)state_attr->values[0].integer;
586
587
break;
588
}
589
}
590
591
ippDelete(response);
592
593
if (!*dest)
594
{
595
syslog(LOG_ERR, "Unable to find \"%s\" in list of printers!", name);
596
597
return (-1);
598
}
599
600
name = dest;
601
}
602
else
603
{
604
/*
605
* Get values from the response...
606
*/
607
608
if (accepting)
609
{
610
if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
611
IPP_TAG_BOOLEAN)) == NULL)
612
syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in "
613
"response from server!");
614
else
615
*accepting = attr->values[0].boolean;
616
}
617
618
if (shared)
619
{
620
if ((attr = ippFindAttribute(response, "printer-is-shared",
621
IPP_TAG_BOOLEAN)) == NULL)
622
{
623
syslog(LOG_ERR, "No printer-is-shared attribute found in "
624
"response from server!");
625
*shared = 1;
626
}
627
else
628
*shared = attr->values[0].boolean;
629
}
630
631
if (state)
632
{
633
if ((attr = ippFindAttribute(response, "printer-state",
634
IPP_TAG_ENUM)) == NULL)
635
syslog(LOG_ERR, "No printer-state attribute found in "
636
"response from server!");
637
else
638
*state = (ipp_pstate_t)attr->values[0].integer;
639
}
640
641
ippDelete(response);
642
}
643
644
/*
645
* Next look for the printer in the lpoptions file...
646
*/
647
648
num_options = 0;
649
650
if (options && shared && accepting)
651
{
652
if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
653
cups_serverroot = CUPS_SERVERROOT;
654
655
snprintf(line, sizeof(line), "%s/lpoptions", cups_serverroot);
656
if ((fp = cupsFileOpen(line, "r")) != NULL)
657
{
658
linenum = 0;
659
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
660
{
661
/*
662
* Make sure we have "Dest name options" or "Default name options"...
663
*/
664
665
if ((_cups_strcasecmp(line, "Dest") && _cups_strcasecmp(line, "Default")) || !value)
666
continue;
667
668
/*
669
* Separate destination name from options...
670
*/
671
672
for (optptr = value; *optptr && !isspace(*optptr & 255); optptr ++);
673
674
while (*optptr == ' ')
675
*optptr++ = '\0';
676
677
/*
678
* If this is our destination, parse the options and break out of
679
* the loop - we're done!
680
*/
681
682
if (!_cups_strcasecmp(value, name))
683
{
684
num_options = cupsParseOptions(optptr, num_options, options);
685
break;
686
}
687
}
688
689
cupsFileClose(fp);
690
}
691
}
692
else if (options)
693
*options = NULL;
694
695
/*
696
* Return the number of options for this destination...
697
*/
698
699
return (num_options);
700
}
701
702
703
/*
704
* 'print_file()' - Add a file to the current job.
705
*/
706
707
static int /* O - 0 on success, -1 on failure */
708
print_file(http_t *http, /* I - HTTP connection */
709
int id, /* I - Job ID */
710
const char *filename, /* I - File to print */
711
const char *docname, /* I - document-name */
712
const char *user, /* I - requesting-user-name */
713
const char *format, /* I - document-format */
714
int last) /* I - 1 = last file in job */
715
{
716
ipp_t *request; /* IPP request */
717
char uri[HTTP_MAX_URI]; /* Printer URI */
718
719
720
/*
721
* Setup the Send-Document request...
722
*/
723
724
request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
725
726
snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
727
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
728
729
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
730
"requesting-user-name", NULL, user);
731
732
if (docname)
733
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
734
"document-name", NULL, docname);
735
736
if (format)
737
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
738
"document-format", NULL, format);
739
740
ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last);
741
742
/*
743
* Do the request...
744
*/
745
746
snprintf(uri, sizeof(uri), "/jobs/%d", id);
747
748
ippDelete(cupsDoFileRequest(http, request, uri, filename));
749
750
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
751
{
752
syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString());
753
754
return (-1);
755
}
756
757
return (0);
758
}
759
760
761
/*
762
* 'recv_print_job()' - Receive a print job from the client.
763
*/
764
765
static int /* O - Command status */
766
recv_print_job(
767
const char *queue, /* I - Printer name */
768
int num_defaults, /* I - Number of default options */
769
cups_option_t *defaults) /* I - Default options */
770
{
771
http_t *http; /* HTTP connection */
772
int i; /* Looping var */
773
int status; /* Command status */
774
int fd; /* Temporary file */
775
FILE *fp; /* File pointer */
776
char filename[1024]; /* Temporary filename */
777
ssize_t bytes; /* Bytes received */
778
size_t total; /* Total bytes */
779
char line[256], /* Line from file/stdin */
780
command, /* Command from line */
781
*count, /* Number of bytes */
782
*name; /* Name of file */
783
const char *job_sheets; /* Job sheets */
784
int num_data; /* Number of data files */
785
char control[1024], /* Control filename */
786
data[100][256], /* Data files */
787
temp[100][1024]; /* Temporary files */
788
char user[1024], /* User name */
789
title[1024], /* Job title */
790
docname[1024], /* Document name */
791
dest[256]; /* Printer/class queue */
792
int accepting, /* printer-is-accepting */
793
shared, /* printer-is-shared */
794
num_options; /* Number of options */
795
cups_option_t *options; /* Options */
796
int id; /* Job ID */
797
int docnumber, /* Current document number */
798
doccount; /* Count of documents */
799
800
801
/*
802
* Connect to the server...
803
*/
804
805
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
806
if (!http)
807
{
808
syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
809
810
putchar(1);
811
812
return (1);
813
}
814
815
/*
816
* See if the printer is available...
817
*/
818
819
num_options = get_printer(http, queue, dest, sizeof(dest), &options,
820
&accepting, &shared, NULL);
821
822
if (num_options < 0 || !accepting || !shared)
823
{
824
if (dest[0])
825
syslog(LOG_INFO, "Rejecting job because \"%s\" is not %s", dest,
826
!accepting ? "accepting jobs" : "shared");
827
else
828
syslog(LOG_ERR, "Unable to get printer information for \"%s\"", queue);
829
830
httpClose(http);
831
832
putchar(1);
833
834
return (1);
835
}
836
837
putchar(0); /* OK so far... */
838
839
/*
840
* Read the request...
841
*/
842
843
status = 0;
844
num_data = 0;
845
fd = -1;
846
847
control[0] = '\0';
848
849
while (smart_gets(line, sizeof(line), stdin) != NULL)
850
{
851
if (strlen(line) < 2)
852
{
853
status = 1;
854
break;
855
}
856
857
command = line[0];
858
count = line + 1;
859
860
for (name = count + 1; *name && !isspace(*name & 255); name ++);
861
while (isspace(*name & 255))
862
*name++ = '\0';
863
864
switch (command)
865
{
866
default :
867
case 0x01 : /* Abort */
868
status = 1;
869
break;
870
871
case 0x02 : /* Receive control file */
872
if (strlen(name) < 2)
873
{
874
syslog(LOG_ERR, "Bad control file name \"%s\"", name);
875
putchar(1);
876
status = 1;
877
break;
878
}
879
880
if (control[0])
881
{
882
/*
883
* Append to the existing control file - the LPD spec is
884
* not entirely clear, but at least the OS/2 LPD code sends
885
* multiple control files per connection...
886
*/
887
888
if ((fd = open(control, O_WRONLY)) < 0)
889
{
890
syslog(LOG_ERR,
891
"Unable to append to temporary control file \"%s\" - %s",
892
control, strerror(errno));
893
putchar(1);
894
status = 1;
895
break;
896
}
897
898
lseek(fd, 0, SEEK_END);
899
}
900
else
901
{
902
if ((fd = cupsTempFd(control, sizeof(control))) < 0)
903
{
904
syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
905
control, strerror(errno));
906
putchar(1);
907
status = 1;
908
break;
909
}
910
911
strlcpy(filename, control, sizeof(filename));
912
}
913
break;
914
915
case 0x03 : /* Receive data file */
916
if (strlen(name) < 2)
917
{
918
syslog(LOG_ERR, "Bad data file name \"%s\"", name);
919
putchar(1);
920
status = 1;
921
break;
922
}
923
924
if (num_data >= (int)(sizeof(data) / sizeof(data[0])))
925
{
926
/*
927
* Too many data files...
928
*/
929
930
syslog(LOG_ERR, "Too many data files (%d)", num_data);
931
putchar(1);
932
status = 1;
933
break;
934
}
935
936
strlcpy(data[num_data], name, sizeof(data[0]));
937
938
if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
939
{
940
syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
941
temp[num_data], strerror(errno));
942
putchar(1);
943
status = 1;
944
break;
945
}
946
947
strlcpy(filename, temp[num_data], sizeof(filename));
948
949
num_data ++;
950
break;
951
}
952
953
putchar(status);
954
955
if (status)
956
break;
957
958
/*
959
* Copy the data or control file from the client...
960
*/
961
962
for (total = (size_t)strtoll(count, NULL, 10); total > 0; total -= (size_t)bytes)
963
{
964
if (total > sizeof(line))
965
bytes = (ssize_t)sizeof(line);
966
else
967
bytes = (ssize_t)total;
968
969
if ((bytes = (ssize_t)fread(line, 1, (size_t)bytes, stdin)) > 0)
970
bytes = write(fd, line, (size_t)bytes);
971
972
if (bytes < 1)
973
{
974
syslog(LOG_ERR, "Error while reading file - %s",
975
strerror(errno));
976
status = 1;
977
break;
978
}
979
}
980
981
/*
982
* Read trailing nul...
983
*/
984
985
if (!status)
986
{
987
if (fread(line, 1, 1, stdin) < 1)
988
{
989
status = 1;
990
syslog(LOG_ERR, "Error while reading trailing nul - %s",
991
strerror(errno));
992
}
993
else if (line[0])
994
{
995
status = 1;
996
syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!",
997
line[0]);
998
}
999
}
1000
1001
/*
1002
* Close the file and send an acknowledgement...
1003
*/
1004
1005
close(fd);
1006
1007
putchar(status);
1008
1009
if (status)
1010
break;
1011
}
1012
1013
if (!status)
1014
{
1015
/*
1016
* Process the control file and print stuff...
1017
*/
1018
1019
if ((fp = fopen(control, "rb")) == NULL)
1020
status = 1;
1021
else
1022
{
1023
/*
1024
* Copy the default options...
1025
*/
1026
1027
for (i = 0; i < num_defaults; i ++)
1028
num_options = cupsAddOption(defaults[i].name,
1029
defaults[i].value,
1030
num_options, &options);
1031
1032
/*
1033
* Grab the job information...
1034
*/
1035
1036
title[0] = '\0';
1037
user[0] = '\0';
1038
docname[0] = '\0';
1039
doccount = 0;
1040
1041
while (smart_gets(line, sizeof(line), fp) != NULL)
1042
{
1043
/*
1044
* Process control lines...
1045
*/
1046
1047
switch (line[0])
1048
{
1049
case 'J' : /* Job name */
1050
smart_strlcpy(title, line + 1, sizeof(title));
1051
break;
1052
1053
case 'N' : /* Document name */
1054
smart_strlcpy(docname, line + 1, sizeof(docname));
1055
break;
1056
1057
case 'P' : /* User identification */
1058
smart_strlcpy(user, line + 1, sizeof(user));
1059
break;
1060
1061
case 'L' : /* Print banner page */
1062
/*
1063
* If a banner was requested and it's not overridden by a
1064
* command line option and the destination's default is none
1065
* then add the standard banner...
1066
*/
1067
1068
if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL &&
1069
((job_sheets = cupsGetOption("job-sheets", num_options,
1070
options)) == NULL ||
1071
!strcmp(job_sheets, "none,none")))
1072
{
1073
num_options = cupsAddOption("job-sheets", "standard",
1074
num_options, &options);
1075
}
1076
break;
1077
1078
case 'c' : /* Plot CIF file */
1079
case 'd' : /* Print DVI file */
1080
case 'f' : /* Print formatted file */
1081
case 'g' : /* Plot file */
1082
case 'l' : /* Print file leaving control characters (raw) */
1083
case 'n' : /* Print ditroff output file */
1084
case 'o' : /* Print PostScript output file */
1085
case 'p' : /* Print file with 'pr' format (prettyprint) */
1086
case 'r' : /* File to print with FORTRAN carriage control */
1087
case 't' : /* Print troff output file */
1088
case 'v' : /* Print raster file */
1089
doccount ++;
1090
1091
if (line[0] == 'l' &&
1092
!cupsGetOption("document-format", num_options, options))
1093
num_options = cupsAddOption("raw", "", num_options, &options);
1094
1095
if (line[0] == 'p')
1096
num_options = cupsAddOption("prettyprint", "", num_options,
1097
&options);
1098
break;
1099
}
1100
}
1101
1102
/*
1103
* Check that we have a username...
1104
*/
1105
1106
if (!user[0])
1107
{
1108
syslog(LOG_WARNING, "No username specified by client! "
1109
"Using \"anonymous\"...");
1110
strlcpy(user, "anonymous", sizeof(user));
1111
}
1112
1113
/*
1114
* Create the job...
1115
*/
1116
1117
if ((id = create_job(http, dest, title, user, num_options, options)) < 0)
1118
status = 1;
1119
else
1120
{
1121
/*
1122
* Then print the job files...
1123
*/
1124
1125
rewind(fp);
1126
1127
docname[0] = '\0';
1128
docnumber = 0;
1129
1130
while (smart_gets(line, sizeof(line), fp) != NULL)
1131
{
1132
/*
1133
* Process control lines...
1134
*/
1135
1136
switch (line[0])
1137
{
1138
case 'N' : /* Document name */
1139
smart_strlcpy(docname, line + 1, sizeof(docname));
1140
break;
1141
1142
case 'c' : /* Plot CIF file */
1143
case 'd' : /* Print DVI file */
1144
case 'f' : /* Print formatted file */
1145
case 'g' : /* Plot file */
1146
case 'l' : /* Print file leaving control characters (raw) */
1147
case 'n' : /* Print ditroff output file */
1148
case 'o' : /* Print PostScript output file */
1149
case 'p' : /* Print file with 'pr' format (prettyprint) */
1150
case 'r' : /* File to print with FORTRAN carriage control */
1151
case 't' : /* Print troff output file */
1152
case 'v' : /* Print raster file */
1153
/*
1154
* Figure out which file we are printing...
1155
*/
1156
1157
for (i = 0; i < num_data; i ++)
1158
if (!strcmp(data[i], line + 1))
1159
break;
1160
1161
if (i >= num_data)
1162
{
1163
status = 1;
1164
break;
1165
}
1166
1167
/*
1168
* Send the print file...
1169
*/
1170
1171
docnumber ++;
1172
1173
if (print_file(http, id, temp[i], docname, user,
1174
cupsGetOption("document-format", num_options,
1175
options),
1176
docnumber == doccount))
1177
status = 1;
1178
else
1179
status = 0;
1180
1181
break;
1182
}
1183
1184
if (status)
1185
break;
1186
}
1187
}
1188
1189
fclose(fp);
1190
}
1191
}
1192
1193
cupsFreeOptions(num_options, options);
1194
1195
httpClose(http);
1196
1197
/*
1198
* Clean up all temporary files and return...
1199
*/
1200
1201
unlink(control);
1202
1203
for (i = 0; i < num_data; i ++)
1204
unlink(temp[i]);
1205
1206
return (status);
1207
}
1208
1209
1210
/*
1211
* 'remove_jobs()' - Cancel one or more jobs.
1212
*/
1213
1214
static int /* O - Command status */
1215
remove_jobs(const char *dest, /* I - Destination */
1216
const char *agent, /* I - User agent */
1217
const char *list) /* I - List of jobs or users */
1218
{
1219
int id; /* Job ID */
1220
http_t *http; /* HTTP server connection */
1221
ipp_t *request; /* IPP Request */
1222
char uri[HTTP_MAX_URI]; /* Job URI */
1223
1224
1225
(void)dest; /* Suppress compiler warnings... */
1226
1227
/*
1228
* Try connecting to the local server...
1229
*/
1230
1231
if ((http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
1232
{
1233
syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1234
strerror(errno));
1235
return (1);
1236
}
1237
1238
/*
1239
* Loop for each job...
1240
*/
1241
1242
while ((id = atoi(list)) > 0)
1243
{
1244
/*
1245
* Skip job ID in list...
1246
*/
1247
1248
while (isdigit(*list & 255))
1249
list ++;
1250
while (isspace(*list & 255))
1251
list ++;
1252
1253
/*
1254
* Build an IPP_OP_CANCEL_JOB request, which requires the following
1255
* attributes:
1256
*
1257
* attributes-charset
1258
* attributes-natural-language
1259
* job-uri
1260
* requesting-user-name
1261
*/
1262
1263
request = ippNewRequest(IPP_OP_CANCEL_JOB);
1264
1265
snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
1266
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1267
1268
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1269
"requesting-user-name", NULL, agent);
1270
1271
/*
1272
* Do the request and get back a response...
1273
*/
1274
1275
ippDelete(cupsDoRequest(http, request, "/jobs"));
1276
1277
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1278
{
1279
syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
1280
cupsLastErrorString());
1281
httpClose(http);
1282
return (1);
1283
}
1284
else
1285
syslog(LOG_INFO, "Job ID %d canceled", id);
1286
}
1287
1288
httpClose(http);
1289
1290
return (0);
1291
}
1292
1293
1294
/*
1295
* 'send_state()' - Send the queue state.
1296
*/
1297
1298
static int /* O - Command status */
1299
send_state(const char *queue, /* I - Destination */
1300
const char *list, /* I - Job or user */
1301
int longstatus) /* I - List of jobs or users */
1302
{
1303
int id; /* Job ID from list */
1304
http_t *http; /* HTTP server connection */
1305
ipp_t *request, /* IPP Request */
1306
*response; /* IPP Response */
1307
ipp_attribute_t *attr; /* Current attribute */
1308
ipp_pstate_t state; /* Printer state */
1309
const char *jobdest, /* Pointer into job-printer-uri */
1310
*jobuser, /* Pointer to job-originating-user-name */
1311
*jobname; /* Pointer to job-name */
1312
ipp_jstate_t jobstate; /* job-state */
1313
int jobid, /* job-id */
1314
jobsize, /* job-k-octets */
1315
jobcount, /* Number of jobs */
1316
jobcopies, /* Number of copies */
1317
rank; /* Rank of job */
1318
char rankstr[255]; /* Rank string */
1319
char namestr[1024]; /* Job name string */
1320
char uri[HTTP_MAX_URI]; /* Printer URI */
1321
char dest[256]; /* Printer/class queue */
1322
static const char * const ranks[10] = /* Ranking strings */
1323
{
1324
"th",
1325
"st",
1326
"nd",
1327
"rd",
1328
"th",
1329
"th",
1330
"th",
1331
"th",
1332
"th",
1333
"th"
1334
};
1335
static const char * const requested[] =
1336
{ /* Requested attributes */
1337
"job-id",
1338
"job-k-octets",
1339
"job-state",
1340
"job-printer-uri",
1341
"job-originating-user-name",
1342
"job-name",
1343
"copies"
1344
};
1345
1346
1347
/*
1348
* Try connecting to the local server...
1349
*/
1350
1351
if ((http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
1352
{
1353
syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1354
strerror(errno));
1355
printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno));
1356
return (1);
1357
}
1358
1359
/*
1360
* Get the actual destination name and printer state...
1361
*/
1362
1363
if (get_printer(http, queue, dest, sizeof(dest), NULL, NULL, NULL, &state))
1364
{
1365
syslog(LOG_ERR, "Unable to get printer %s: %s", queue,
1366
cupsLastErrorString());
1367
printf("Unable to get printer %s: %s", queue, cupsLastErrorString());
1368
return (1);
1369
}
1370
1371
/*
1372
* Show the queue state...
1373
*/
1374
1375
switch (state)
1376
{
1377
case IPP_PSTATE_IDLE :
1378
printf("%s is ready\n", dest);
1379
break;
1380
case IPP_PSTATE_PROCESSING :
1381
printf("%s is ready and printing\n", dest);
1382
break;
1383
case IPP_PSTATE_STOPPED :
1384
printf("%s is not ready\n", dest);
1385
break;
1386
}
1387
1388
/*
1389
* Build an IPP_OP_GET_JOBS or IPP_OP_GET_JOB_ATTRIBUTES request, which requires
1390
* the following attributes:
1391
*
1392
* attributes-charset
1393
* attributes-natural-language
1394
* job-uri or printer-uri
1395
*/
1396
1397
id = atoi(list);
1398
1399
request = ippNewRequest(id ? IPP_OP_GET_JOB_ATTRIBUTES : IPP_OP_GET_JOBS);
1400
1401
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1402
"localhost", 0, "/printers/%s", dest);
1403
1404
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1405
NULL, uri);
1406
1407
if (id)
1408
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1409
else
1410
{
1411
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1412
"requesting-user-name", NULL, list);
1413
ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1414
}
1415
1416
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1417
"requested-attributes",
1418
sizeof(requested) / sizeof(requested[0]),
1419
NULL, requested);
1420
1421
/*
1422
* Do the request and get back a response...
1423
*/
1424
1425
jobcount = 0;
1426
response = cupsDoRequest(http, request, "/");
1427
1428
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1429
{
1430
printf("get-jobs failed: %s\n", cupsLastErrorString());
1431
ippDelete(response);
1432
return (1);
1433
}
1434
1435
/*
1436
* Loop through the job list and display them...
1437
*/
1438
1439
for (attr = response->attrs, rank = 1; attr; attr = attr->next)
1440
{
1441
/*
1442
* Skip leading attributes until we hit a job...
1443
*/
1444
1445
while (attr && (attr->group_tag != IPP_TAG_JOB || !attr->name))
1446
attr = attr->next;
1447
1448
if (!attr)
1449
break;
1450
1451
/*
1452
* Pull the needed attributes from this job...
1453
*/
1454
1455
jobid = 0;
1456
jobsize = 0;
1457
jobstate = IPP_JSTATE_PENDING;
1458
jobname = "untitled";
1459
jobuser = NULL;
1460
jobdest = NULL;
1461
jobcopies = 1;
1462
1463
while (attr && attr->group_tag == IPP_TAG_JOB)
1464
{
1465
if (!strcmp(attr->name, "job-id") &&
1466
attr->value_tag == IPP_TAG_INTEGER)
1467
jobid = attr->values[0].integer;
1468
1469
if (!strcmp(attr->name, "job-k-octets") &&
1470
attr->value_tag == IPP_TAG_INTEGER)
1471
jobsize = attr->values[0].integer;
1472
1473
if (!strcmp(attr->name, "job-state") &&
1474
attr->value_tag == IPP_TAG_ENUM)
1475
jobstate = (ipp_jstate_t)attr->values[0].integer;
1476
1477
if (!strcmp(attr->name, "job-printer-uri") &&
1478
attr->value_tag == IPP_TAG_URI)
1479
if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
1480
jobdest ++;
1481
1482
if (!strcmp(attr->name, "job-originating-user-name") &&
1483
attr->value_tag == IPP_TAG_NAME)
1484
jobuser = attr->values[0].string.text;
1485
1486
if (!strcmp(attr->name, "job-name") &&
1487
attr->value_tag == IPP_TAG_NAME)
1488
jobname = attr->values[0].string.text;
1489
1490
if (!strcmp(attr->name, "copies") &&
1491
attr->value_tag == IPP_TAG_INTEGER)
1492
jobcopies = attr->values[0].integer;
1493
1494
attr = attr->next;
1495
}
1496
1497
/*
1498
* See if we have everything needed...
1499
*/
1500
1501
if (!jobdest || !jobid)
1502
{
1503
if (!attr)
1504
break;
1505
else
1506
continue;
1507
}
1508
1509
if (!longstatus && jobcount == 0)
1510
puts("Rank Owner Job File(s) Total Size");
1511
1512
jobcount ++;
1513
1514
/*
1515
* Display the job...
1516
*/
1517
1518
if (jobstate == IPP_JSTATE_PROCESSING)
1519
strlcpy(rankstr, "active", sizeof(rankstr));
1520
else
1521
{
1522
snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
1523
rank ++;
1524
}
1525
1526
if (longstatus)
1527
{
1528
puts("");
1529
1530
if (jobcopies > 1)
1531
snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
1532
jobname);
1533
else
1534
strlcpy(namestr, jobname, sizeof(namestr));
1535
1536
printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
1537
printf(" %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
1538
}
1539
else
1540
printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
1541
jobid, jobname, 1024.0 * jobsize);
1542
1543
if (!attr)
1544
break;
1545
}
1546
1547
ippDelete(response);
1548
1549
if (jobcount == 0)
1550
puts("no entries");
1551
1552
httpClose(http);
1553
1554
return (0);
1555
}
1556
1557
1558
/*
1559
* 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1560
*/
1561
1562
static char * /* O - Line read or NULL */
1563
smart_gets(char *s, /* I - Pointer to line buffer */
1564
int len, /* I - Size of line buffer */
1565
FILE *fp) /* I - File to read from */
1566
{
1567
char *ptr, /* Pointer into line */
1568
*end; /* End of line */
1569
int ch; /* Character from file */
1570
1571
1572
/*
1573
* Read the line; unlike fgets(), we read the entire line but dump
1574
* characters that go past the end of the buffer. Also, we accept
1575
* CR, LF, or CR LF for the line endings to be "safe", although
1576
* RFC 1179 specifically says "just use LF".
1577
*/
1578
1579
ptr = s;
1580
end = s + len - 1;
1581
1582
while ((ch = getc(fp)) != EOF)
1583
{
1584
if (ch == '\n')
1585
break;
1586
else if (ch == '\r')
1587
{
1588
/*
1589
* See if a LF follows...
1590
*/
1591
1592
ch = getc(fp);
1593
1594
if (ch != '\n')
1595
ungetc(ch, fp);
1596
1597
break;
1598
}
1599
else if (ptr < end)
1600
*ptr++ = (char)ch;
1601
}
1602
1603
*ptr = '\0';
1604
1605
if (ch == EOF && ptr == s)
1606
return (NULL);
1607
else
1608
return (s);
1609
}
1610
1611
1612
/*
1613
* 'smart_strlcpy()' - Copy a string and convert from ISO-8859-1 to UTF-8 as needed.
1614
*/
1615
1616
static void
1617
smart_strlcpy(char *dst, /* I - Output buffer */
1618
const char *src, /* I - Input string */
1619
size_t dstsize) /* I - Size of output buffer */
1620
{
1621
const unsigned char *srcptr; /* Pointer into input string */
1622
unsigned char *dstptr, /* Pointer into output buffer */
1623
*dstend; /* End of output buffer */
1624
int saw_8859 = 0; /* Saw an extended character that was not UTF-8? */
1625
1626
1627
for (srcptr = (unsigned char *)src, dstptr = (unsigned char *)dst, dstend = dstptr + dstsize - 1; *srcptr;)
1628
{
1629
if (*srcptr < 0x80)
1630
*dstptr++ = *srcptr++; /* ASCII */
1631
else if (saw_8859)
1632
{
1633
/*
1634
* Map ISO-8859-1 (most likely character set for legacy LPD clients) to
1635
* UTF-8...
1636
*/
1637
1638
if (dstptr > (dstend - 2))
1639
break;
1640
1641
*dstptr++ = 0xc0 | (*srcptr >> 6);
1642
*dstptr++ = 0x80 | (*srcptr++ & 0x3f);
1643
}
1644
else if ((*srcptr & 0xe0) == 0xc0 && (srcptr[1] & 0xc0) == 0x80)
1645
{
1646
/*
1647
* 2-byte UTF-8 sequence...
1648
*/
1649
1650
if (dstptr > (dstend - 2))
1651
break;
1652
1653
*dstptr++ = *srcptr++;
1654
*dstptr++ = *srcptr++;
1655
}
1656
else if ((*srcptr & 0xf0) == 0xe0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80)
1657
{
1658
/*
1659
* 3-byte UTF-8 sequence...
1660
*/
1661
1662
if (dstptr > (dstend - 3))
1663
break;
1664
1665
*dstptr++ = *srcptr++;
1666
*dstptr++ = *srcptr++;
1667
*dstptr++ = *srcptr++;
1668
}
1669
else if ((*srcptr & 0xf8) == 0xf0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80 && (srcptr[3] & 0xc0) == 0x80)
1670
{
1671
/*
1672
* 4-byte UTF-8 sequence...
1673
*/
1674
1675
if (dstptr > (dstend - 4))
1676
break;
1677
1678
*dstptr++ = *srcptr++;
1679
*dstptr++ = *srcptr++;
1680
*dstptr++ = *srcptr++;
1681
*dstptr++ = *srcptr++;
1682
}
1683
else
1684
{
1685
/*
1686
* Bad UTF-8 sequence, this must be an ISO-8859-1 string...
1687
*/
1688
1689
saw_8859 = 1;
1690
1691
if (dstptr > (dstend - 2))
1692
break;
1693
1694
*dstptr++ = 0xc0 | (*srcptr >> 6);
1695
*dstptr++ = 0x80 | (*srcptr++ & 0x3f);
1696
}
1697
}
1698
1699
*dstptr = '\0';
1700
}
1701
1702