]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest.c
Merge changes from CUPS 1.5b1-r9798.
[thirdparty/cups.git] / cups / dest.c
1 /*
2 * "$Id: dest.c 9568 2011-02-25 06:13:56Z mike $"
3 *
4 * User-defined destination (and option) support for CUPS.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * cupsAddDest() - Add a destination to the list of
20 * destinations.
21 * _cupsAppleCopyDefaultPaperID() - Get the default paper ID.
22 * _cupsAppleCopyDefaultPrinter() - Get the default printer at this location.
23 * _cupsAppleGetUseLastPrinter() - Get whether to use the last used printer.
24 * _cupsAppleSetDefaultPaperID() - Set the default paper id.
25 * _cupsAppleSetDefaultPrinter() - Set the default printer for this location.
26 * _cupsAppleSetUseLastPrinter() - Set whether to use the last used printer.
27 * cupsFreeDests() - Free the memory used by the list of
28 * destinations.
29 * cupsGetDest() - Get the named destination from the list.
30 * _cupsGetDests() - Get destinations from a server.
31 * cupsGetDests() - Get the list of destinations from the
32 * default server.
33 * cupsGetDests2() - Get the list of destinations from the
34 * specified server.
35 * cupsGetNamedDest() - Get options for the named destination.
36 * cupsRemoveDest() - Remove a destination from the destination
37 * list.
38 * cupsSetDefaultDest() - Set the default destination.
39 * cupsSetDests() - Save the list of destinations for the
40 * default server.
41 * cupsSetDests2() - Save the list of destinations for the
42 * specified server.
43 * _cupsUserDefault() - Get the user default printer from
44 * environment variables and location
45 * information.
46 * appleCopyLocations() - Copy the location history array.
47 * appleCopyNetwork() - Get the network ID for the current
48 * location.
49 * appleGetPaperSize() - Get the default paper size.
50 * appleGetPrinter() - Get a printer from the history array.
51 * cups_add_dest() - Add a destination to the array.
52 * cups_compare_dests() - Compare two destinations.
53 * cups_find_dest() - Find a destination using a binary search.
54 * cups_get_default() - Get the default destination from an
55 * lpoptions file.
56 * cups_get_dests() - Get destinations from a file.
57 * cups_make_string() - Make a comma-separated string of values
58 * from an IPP attribute.
59 */
60
61 /*
62 * Include necessary headers...
63 */
64
65 #include "cups-private.h"
66 #include <sys/stat.h>
67
68 #ifdef HAVE_NOTIFY_H
69 # include <notify.h>
70 #endif /* HAVE_NOTIFY_H */
71
72 #ifdef __APPLE__
73 # include <SystemConfiguration/SystemConfiguration.h>
74 # define kCUPSPrintingPrefs CFSTR("org.cups.PrintingPrefs")
75 # define kDefaultPaperIDKey CFSTR("DefaultPaperID")
76 # define kLastUsedPrintersKey CFSTR("LastUsedPrinters")
77 # define kLocationNetworkKey CFSTR("Network")
78 # define kLocationPrinterIDKey CFSTR("PrinterID")
79 # define kUseLastPrinter CFSTR("UseLastPrinter")
80 #endif /* __APPLE__ */
81
82
83 /*
84 * Local functions...
85 */
86
87 #ifdef __APPLE__
88 static CFArrayRef appleCopyLocations(void);
89 static CFStringRef appleCopyNetwork(void);
90 static char *appleGetPaperSize(char *name, int namesize);
91 static CFStringRef appleGetPrinter(CFArrayRef locations, CFStringRef network,
92 CFIndex *locindex);
93 #endif /* __APPLE__ */
94 static cups_dest_t *cups_add_dest(const char *name, const char *instance,
95 int *num_dests, cups_dest_t **dests);
96 static int cups_compare_dests(cups_dest_t *a, cups_dest_t *b);
97 static int cups_find_dest(const char *name, const char *instance,
98 int num_dests, cups_dest_t *dests, int prev,
99 int *rdiff);
100 static char *cups_get_default(const char *filename, char *namebuf,
101 size_t namesize, const char **instance);
102 static int cups_get_dests(const char *filename, const char *match_name,
103 const char *match_inst, int user_default_set,
104 int num_dests, cups_dest_t **dests);
105 static char *cups_make_string(ipp_attribute_t *attr, char *buffer,
106 size_t bufsize);
107
108
109 /*
110 * 'cupsAddDest()' - Add a destination to the list of destinations.
111 *
112 * This function cannot be used to add a new class or printer queue,
113 * it only adds a new container of saved options for the named
114 * destination or instance.
115 *
116 * If the named destination already exists, the destination list is
117 * returned unchanged. Adding a new instance of a destination creates
118 * a copy of that destination's options.
119 *
120 * Use the @link cupsSaveDests@ function to save the updated list of
121 * destinations to the user's lpoptions file.
122 */
123
124 int /* O - New number of destinations */
125 cupsAddDest(const char *name, /* I - Destination name */
126 const char *instance, /* I - Instance name or @code NULL@ for none/primary */
127 int num_dests, /* I - Number of destinations */
128 cups_dest_t **dests) /* IO - Destinations */
129 {
130 int i; /* Looping var */
131 cups_dest_t *dest; /* Destination pointer */
132 cups_dest_t *parent = NULL; /* Parent destination */
133 cups_option_t *doption, /* Current destination option */
134 *poption; /* Current parent option */
135
136
137 if (!name || !dests)
138 return (0);
139
140 if (!cupsGetDest(name, instance, num_dests, *dests))
141 {
142 if (instance && !cupsGetDest(name, NULL, num_dests, *dests))
143 return (num_dests);
144
145 dest = cups_add_dest(name, instance, &num_dests, dests);
146
147 /*
148 * Find the base dest again now the array has been realloc'd.
149 */
150
151 parent = cupsGetDest(name, NULL, num_dests, *dests);
152
153 if (instance && parent && parent->num_options > 0)
154 {
155 /*
156 * Copy options from parent...
157 */
158
159 dest->options = calloc(sizeof(cups_option_t), parent->num_options);
160
161 if (dest->options)
162 {
163 dest->num_options = parent->num_options;
164
165 for (i = dest->num_options, doption = dest->options,
166 poption = parent->options;
167 i > 0;
168 i --, doption ++, poption ++)
169 {
170 doption->name = _cupsStrRetain(poption->name);
171 doption->value = _cupsStrRetain(poption->value);
172 }
173 }
174 }
175 }
176
177 return (num_dests);
178 }
179
180
181 #ifdef __APPLE__
182 /*
183 * '_cupsAppleCopyDefaultPaperID()' - Get the default paper ID.
184 */
185
186 CFStringRef /* O - Default paper ID */
187 _cupsAppleCopyDefaultPaperID(void)
188 {
189 return (CFPreferencesCopyAppValue(kDefaultPaperIDKey,
190 kCUPSPrintingPrefs));
191 }
192
193
194 /*
195 * '_cupsAppleCopyDefaultPrinter()' - Get the default printer at this location.
196 */
197
198 CFStringRef /* O - Default printer name */
199 _cupsAppleCopyDefaultPrinter(void)
200 {
201 CFStringRef network; /* Network location */
202 CFArrayRef locations; /* Location array */
203 CFStringRef locprinter; /* Current printer */
204
205
206 /*
207 * Use location-based defaults only if "use last printer" is selected in the
208 * system preferences...
209 */
210
211 if (!_cupsAppleGetUseLastPrinter())
212 {
213 DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Not using last printer as "
214 "default.");
215 return (NULL);
216 }
217
218 /*
219 * Get the current location...
220 */
221
222 if ((network = appleCopyNetwork()) == NULL)
223 {
224 DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Unable to get current "
225 "network.");
226 return (NULL);
227 }
228
229 //# ifdef DEBUG
230 // CFStringGetCString(network, name, namesize, kCFStringEncodingUTF8);
231 // DEBUG_printf(("2_cupsUserDefault: network=\"%s\"", name));
232 //# endif /* DEBUG */
233
234 /*
235 * Lookup the network in the preferences...
236 */
237
238 if ((locations = appleCopyLocations()) == NULL)
239 {
240 /*
241 * Missing or bad location array, so no location-based default...
242 */
243
244 DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Missing or bad last used "
245 "printer array.");
246
247 CFRelease(network);
248
249 return (NULL);
250 }
251
252 DEBUG_printf(("1_cupsAppleCopyDefaultPrinter: Got locations, %d entries.",
253 (int)CFArrayGetCount(locations)));
254
255 if ((locprinter = appleGetPrinter(locations, network, NULL)) != NULL)
256 CFRetain(locprinter);
257
258 CFRelease(network);
259 CFRelease(locations);
260
261 return (locprinter);
262 }
263
264
265 /*
266 * '_cupsAppleGetUseLastPrinter()' - Get whether to use the last used printer.
267 */
268
269 int /* O - 1 to use last printer, 0 otherwise */
270 _cupsAppleGetUseLastPrinter(void)
271 {
272 Boolean uselast, /* Use last printer preference value */
273 uselast_set; /* Valid is set? */
274
275
276 if (getenv("CUPS_DISABLE_APPLE_DEFAULT"))
277 return (0);
278
279 uselast = CFPreferencesGetAppBooleanValue(kUseLastPrinter,
280 kCUPSPrintingPrefs,
281 &uselast_set);
282 if (!uselast_set)
283 return (1);
284 else
285 return (uselast);
286 }
287
288
289 /*
290 * '_cupsAppleSetDefaultPaperID()' - Set the default paper id.
291 */
292
293 void
294 _cupsAppleSetDefaultPaperID(
295 CFStringRef name) /* I - New paper ID */
296 {
297 CFPreferencesSetAppValue(kDefaultPaperIDKey, name, kCUPSPrintingPrefs);
298 CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
299 notify_post("com.apple.printerPrefsChange");
300 }
301
302
303 /*
304 * '_cupsAppleSetDefaultPrinter()' - Set the default printer for this location.
305 */
306
307 void
308 _cupsAppleSetDefaultPrinter(
309 CFStringRef name) /* I - Default printer/class name */
310 {
311 CFStringRef network; /* Current network */
312 CFArrayRef locations; /* Old locations array */
313 CFIndex locindex; /* Index in locations array */
314 CFStringRef locprinter; /* Current printer */
315 CFMutableArrayRef newlocations; /* New locations array */
316 CFMutableDictionaryRef newlocation; /* New location */
317
318
319 /*
320 * Get the current location...
321 */
322
323 if ((network = appleCopyNetwork()) == NULL)
324 {
325 DEBUG_puts("1_cupsAppleSetDefaultPrinter: Unable to get current network...");
326 return;
327 }
328
329 /*
330 * Lookup the network in the preferences...
331 */
332
333 if ((locations = appleCopyLocations()) != NULL)
334 locprinter = appleGetPrinter(locations, network, &locindex);
335 else
336 {
337 locprinter = NULL;
338 locindex = -1;
339 }
340
341 if (!locprinter || CFStringCompare(locprinter, name, 0) != kCFCompareEqualTo)
342 {
343 /*
344 * Need to change the locations array...
345 */
346
347 if (locations)
348 {
349 newlocations = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
350 locations);
351
352 if (locprinter)
353 CFArrayRemoveValueAtIndex(newlocations, locindex);
354 }
355 else
356 newlocations = CFArrayCreateMutable(kCFAllocatorDefault, 0,
357 &kCFTypeArrayCallBacks);
358
359 newlocation = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
360 &kCFTypeDictionaryKeyCallBacks,
361 &kCFTypeDictionaryValueCallBacks);
362
363 if (newlocation && newlocations)
364 {
365 /*
366 * Put the new location at the front of the array...
367 */
368
369 CFDictionaryAddValue(newlocation, kLocationNetworkKey, network);
370 CFDictionaryAddValue(newlocation, kLocationPrinterIDKey, name);
371 CFArrayInsertValueAtIndex(newlocations, 0, newlocation);
372
373 /*
374 * Limit the number of locations to 10...
375 */
376
377 while (CFArrayGetCount(newlocations) > 10)
378 CFArrayRemoveValueAtIndex(newlocations, 10);
379
380 /*
381 * Push the changes out...
382 */
383
384 CFPreferencesSetAppValue(kLastUsedPrintersKey, newlocations,
385 kCUPSPrintingPrefs);
386 CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
387 notify_post("com.apple.printerPrefsChange");
388 }
389
390 if (newlocations)
391 CFRelease(newlocations);
392
393 if (newlocation)
394 CFRelease(newlocation);
395 }
396
397 if (locations)
398 CFRelease(locations);
399
400 CFRelease(network);
401 }
402
403
404 /*
405 * '_cupsAppleSetUseLastPrinter()' - Set whether to use the last used printer.
406 */
407
408 void
409 _cupsAppleSetUseLastPrinter(
410 int uselast) /* O - 1 to use last printer, 0 otherwise */
411 {
412 CFPreferencesSetAppValue(kUseLastPrinter,
413 uselast ? kCFBooleanTrue : kCFBooleanFalse,
414 kCUPSPrintingPrefs);
415 CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
416 notify_post("com.apple.printerPrefsChange");
417 }
418 #endif /* __APPLE__ */
419
420
421 /*
422 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
423 */
424
425 void
426 cupsFreeDests(int num_dests, /* I - Number of destinations */
427 cups_dest_t *dests) /* I - Destinations */
428 {
429 int i; /* Looping var */
430 cups_dest_t *dest; /* Current destination */
431
432
433 if (num_dests == 0 || dests == NULL)
434 return;
435
436 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
437 {
438 _cupsStrFree(dest->name);
439 _cupsStrFree(dest->instance);
440
441 cupsFreeOptions(dest->num_options, dest->options);
442 }
443
444 free(dests);
445 }
446
447
448 /*
449 * 'cupsGetDest()' - Get the named destination from the list.
450 *
451 * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
452 * list of supported destinations for the current user.
453 */
454
455 cups_dest_t * /* O - Destination pointer or @code NULL@ */
456 cupsGetDest(const char *name, /* I - Destination name or @code NULL@ for the default destination */
457 const char *instance, /* I - Instance name or @code NULL@ */
458 int num_dests, /* I - Number of destinations */
459 cups_dest_t *dests) /* I - Destinations */
460 {
461 int diff, /* Result of comparison */
462 match; /* Matching index */
463
464
465 if (num_dests <= 0 || !dests)
466 return (NULL);
467
468 if (!name)
469 {
470 /*
471 * NULL name for default printer.
472 */
473
474 while (num_dests > 0)
475 {
476 if (dests->is_default)
477 return (dests);
478
479 num_dests --;
480 dests ++;
481 }
482 }
483 else
484 {
485 /*
486 * Lookup name and optionally the instance...
487 */
488
489 match = cups_find_dest(name, instance, num_dests, dests, -1, &diff);
490
491 if (!diff)
492 return (dests + match);
493 }
494
495 return (NULL);
496 }
497
498
499 /*
500 * '_cupsGetDests()' - Get destinations from a server.
501 *
502 * "op" is CUPS_GET_PRINTERS to get a full list, CUPS_GET_DEFAULT to get the
503 * system-wide default printer, or IPP_GET_PRINTER_ATTRIBUTES for a known
504 * printer.
505 *
506 * "name" is the name of an existing printer and is only used when "op" is
507 * IPP_GET_PRINTER_ATTRIBUTES.
508 *
509 * "dest" is initialized to point to the array of destinations.
510 *
511 * 0 is returned if there are no printers, no default printer, or the named
512 * printer does not exist, respectively.
513 *
514 * Free the memory used by the destination array using the @link cupsFreeDests@
515 * function.
516 *
517 * Note: On Mac OS X this function also gets the default paper from the system
518 * preferences (~/L/P/org.cups.PrintingPrefs.plist) and includes it in the
519 * options array for each destination that supports it.
520 */
521
522 int /* O - Number of destinations */
523 _cupsGetDests(http_t *http, /* I - Connection to server or CUPS_HTTP_DEFAULT */
524 ipp_op_t op, /* I - IPP operation */
525 const char *name, /* I - Name of destination */
526 cups_dest_t **dests) /* IO - Destinations */
527 {
528 int num_dests = 0; /* Number of destinations */
529 cups_dest_t *dest; /* Current destination */
530 ipp_t *request, /* IPP Request */
531 *response; /* IPP Response */
532 ipp_attribute_t *attr; /* Current attribute */
533 const char *printer_name; /* printer-name attribute */
534 char uri[1024]; /* printer-uri value */
535 int num_options; /* Number of options */
536 cups_option_t *options; /* Options */
537 #ifdef __APPLE__
538 char media_default[41]; /* Default paper size */
539 #endif /* __APPLE__ */
540 char optname[1024], /* Option name */
541 value[2048], /* Option value */
542 *ptr; /* Pointer into name/value */
543 static const char * const pattrs[] = /* Attributes we're interested in */
544 {
545 "auth-info-required",
546 "device-uri",
547 "job-sheets-default",
548 "marker-change-time",
549 "marker-colors",
550 "marker-high-levels",
551 "marker-levels",
552 "marker-low-levels",
553 "marker-message",
554 "marker-names",
555 "marker-types",
556 #ifdef __APPLE__
557 "media-supported",
558 #endif /* __APPLE__ */
559 "printer-commands",
560 "printer-defaults",
561 "printer-info",
562 "printer-is-accepting-jobs",
563 "printer-is-shared",
564 "printer-location",
565 "printer-make-and-model",
566 "printer-name",
567 "printer-state",
568 "printer-state-change-time",
569 "printer-state-reasons",
570 "printer-type",
571 "printer-uri-supported"
572 };
573
574
575 #ifdef __APPLE__
576 /*
577 * Get the default paper size...
578 */
579
580 appleGetPaperSize(media_default, sizeof(media_default));
581 #endif /* __APPLE__ */
582
583 /*
584 * Build a CUPS_GET_PRINTERS or IPP_GET_PRINTER_ATTRIBUTES request, which
585 * require the following attributes:
586 *
587 * attributes-charset
588 * attributes-natural-language
589 * requesting-user-name
590 * printer-uri [for IPP_GET_PRINTER_ATTRIBUTES]
591 */
592
593 request = ippNewRequest(op);
594
595 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
596 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
597 NULL, pattrs);
598
599 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
600 "requesting-user-name", NULL, cupsUser());
601
602 if (name && op != CUPS_GET_DEFAULT)
603 {
604 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
605 "localhost", ippPort(), "/printers/%s", name);
606 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
607 uri);
608 }
609
610 /*
611 * Do the request and get back a response...
612 */
613
614 if ((response = cupsDoRequest(http, request, "/")) != NULL)
615 {
616 for (attr = response->attrs; attr != NULL; attr = attr->next)
617 {
618 /*
619 * Skip leading attributes until we hit a printer...
620 */
621
622 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
623 attr = attr->next;
624
625 if (attr == NULL)
626 break;
627
628 /*
629 * Pull the needed attributes from this printer...
630 */
631
632 printer_name = NULL;
633 num_options = 0;
634 options = NULL;
635
636 for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
637 {
638 if (attr->value_tag != IPP_TAG_INTEGER &&
639 attr->value_tag != IPP_TAG_ENUM &&
640 attr->value_tag != IPP_TAG_BOOLEAN &&
641 attr->value_tag != IPP_TAG_TEXT &&
642 attr->value_tag != IPP_TAG_TEXTLANG &&
643 attr->value_tag != IPP_TAG_NAME &&
644 attr->value_tag != IPP_TAG_NAMELANG &&
645 attr->value_tag != IPP_TAG_KEYWORD &&
646 attr->value_tag != IPP_TAG_RANGE &&
647 attr->value_tag != IPP_TAG_URI)
648 continue;
649
650 if (!strcmp(attr->name, "auth-info-required") ||
651 !strcmp(attr->name, "device-uri") ||
652 !strcmp(attr->name, "marker-change-time") ||
653 !strcmp(attr->name, "marker-colors") ||
654 !strcmp(attr->name, "marker-high-levels") ||
655 !strcmp(attr->name, "marker-levels") ||
656 !strcmp(attr->name, "marker-low-levels") ||
657 !strcmp(attr->name, "marker-message") ||
658 !strcmp(attr->name, "marker-names") ||
659 !strcmp(attr->name, "marker-types") ||
660 !strcmp(attr->name, "printer-commands") ||
661 !strcmp(attr->name, "printer-info") ||
662 !strcmp(attr->name, "printer-is-shared") ||
663 !strcmp(attr->name, "printer-make-and-model") ||
664 !strcmp(attr->name, "printer-state") ||
665 !strcmp(attr->name, "printer-state-change-time") ||
666 !strcmp(attr->name, "printer-type") ||
667 !strcmp(attr->name, "printer-is-accepting-jobs") ||
668 !strcmp(attr->name, "printer-location") ||
669 !strcmp(attr->name, "printer-state-reasons") ||
670 !strcmp(attr->name, "printer-uri-supported"))
671 {
672 /*
673 * Add a printer description attribute...
674 */
675
676 num_options = cupsAddOption(attr->name,
677 cups_make_string(attr, value,
678 sizeof(value)),
679 num_options, &options);
680 }
681 #ifdef __APPLE__
682 else if (!strcmp(attr->name, "media-supported"))
683 {
684 /*
685 * See if we can set a default media size...
686 */
687
688 int i; /* Looping var */
689
690 for (i = 0; i < attr->num_values; i ++)
691 if (!_cups_strcasecmp(media_default, attr->values[i].string.text))
692 {
693 num_options = cupsAddOption("media", media_default, num_options,
694 &options);
695 break;
696 }
697 }
698 #endif /* __APPLE__ */
699 else if (!strcmp(attr->name, "printer-name") &&
700 attr->value_tag == IPP_TAG_NAME)
701 printer_name = attr->values[0].string.text;
702 else if (strncmp(attr->name, "notify-", 7) &&
703 (attr->value_tag == IPP_TAG_BOOLEAN ||
704 attr->value_tag == IPP_TAG_ENUM ||
705 attr->value_tag == IPP_TAG_INTEGER ||
706 attr->value_tag == IPP_TAG_KEYWORD ||
707 attr->value_tag == IPP_TAG_NAME ||
708 attr->value_tag == IPP_TAG_RANGE) &&
709 (ptr = strstr(attr->name, "-default")) != NULL)
710 {
711 /*
712 * Add a default option...
713 */
714
715 strlcpy(optname, attr->name, sizeof(optname));
716 optname[ptr - attr->name] = '\0';
717
718 if (_cups_strcasecmp(optname, "media") ||
719 !cupsGetOption("media", num_options, options))
720 num_options = cupsAddOption(optname,
721 cups_make_string(attr, value,
722 sizeof(value)),
723 num_options, &options);
724 }
725 }
726
727 /*
728 * See if we have everything needed...
729 */
730
731 if (!printer_name)
732 {
733 cupsFreeOptions(num_options, options);
734
735 if (attr == NULL)
736 break;
737 else
738 continue;
739 }
740
741 if ((dest = cups_add_dest(printer_name, NULL, &num_dests, dests)) != NULL)
742 {
743 dest->num_options = num_options;
744 dest->options = options;
745 }
746 else
747 cupsFreeOptions(num_options, options);
748
749 if (attr == NULL)
750 break;
751 }
752
753 ippDelete(response);
754 }
755
756 /*
757 * Return the count...
758 */
759
760 return (num_dests);
761 }
762
763
764 /*
765 * 'cupsGetDests()' - Get the list of destinations from the default server.
766 *
767 * Starting with CUPS 1.2, the returned list of destinations include the
768 * printer-info, printer-is-accepting-jobs, printer-is-shared,
769 * printer-make-and-model, printer-state, printer-state-change-time,
770 * printer-state-reasons, and printer-type attributes as options. CUPS 1.4
771 * adds the marker-change-time, marker-colors, marker-high-levels,
772 * marker-levels, marker-low-levels, marker-message, marker-names,
773 * marker-types, and printer-commands attributes as well.
774 *
775 * Use the @link cupsFreeDests@ function to free the destination list and
776 * the @link cupsGetDest@ function to find a particular destination.
777 */
778
779 int /* O - Number of destinations */
780 cupsGetDests(cups_dest_t **dests) /* O - Destinations */
781 {
782 return (cupsGetDests2(CUPS_HTTP_DEFAULT, dests));
783 }
784
785
786 /*
787 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
788 *
789 * Starting with CUPS 1.2, the returned list of destinations include the
790 * printer-info, printer-is-accepting-jobs, printer-is-shared,
791 * printer-make-and-model, printer-state, printer-state-change-time,
792 * printer-state-reasons, and printer-type attributes as options. CUPS 1.4
793 * adds the marker-change-time, marker-colors, marker-high-levels,
794 * marker-levels, marker-low-levels, marker-message, marker-names,
795 * marker-types, and printer-commands attributes as well.
796 *
797 * Use the @link cupsFreeDests@ function to free the destination list and
798 * the @link cupsGetDest@ function to find a particular destination.
799 *
800 * @since CUPS 1.1.21/Mac OS X 10.4@
801 */
802
803 int /* O - Number of destinations */
804 cupsGetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
805 cups_dest_t **dests) /* O - Destinations */
806 {
807 int i; /* Looping var */
808 int num_dests; /* Number of destinations */
809 cups_dest_t *dest; /* Destination pointer */
810 const char *home; /* HOME environment variable */
811 char filename[1024]; /* Local ~/.cups/lpoptions file */
812 const char *defprinter; /* Default printer */
813 char name[1024], /* Copy of printer name */
814 *instance, /* Pointer to instance name */
815 *user_default; /* User default printer */
816 int num_reals; /* Number of real queues */
817 cups_dest_t *reals; /* Real queues */
818 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
819
820
821 /*
822 * Range check the input...
823 */
824
825 if (!dests)
826 {
827 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad NULL dests pointer"), 1);
828 return (0);
829 }
830
831 /*
832 * Grab the printers and classes...
833 */
834
835 *dests = (cups_dest_t *)0;
836 num_dests = _cupsGetDests(http, CUPS_GET_PRINTERS, NULL, dests);
837
838 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE)
839 {
840 cupsFreeDests(num_dests, *dests);
841 *dests = (cups_dest_t *)0;
842 return (0);
843 }
844
845 /*
846 * Make a copy of the "real" queues for a later sanity check...
847 */
848
849 if (num_dests > 0)
850 {
851 num_reals = num_dests;
852 reals = calloc(num_reals, sizeof(cups_dest_t));
853
854 if (reals)
855 memcpy(reals, *dests, num_reals * sizeof(cups_dest_t));
856 else
857 num_reals = 0;
858 }
859 else
860 {
861 num_reals = 0;
862 reals = NULL;
863 }
864
865 /*
866 * Grab the default destination...
867 */
868
869 if ((user_default = _cupsUserDefault(name, sizeof(name))) != NULL)
870 defprinter = name;
871 else if ((defprinter = cupsGetDefault2(http)) != NULL)
872 {
873 strlcpy(name, defprinter, sizeof(name));
874 defprinter = name;
875 }
876
877 if (defprinter)
878 {
879 /*
880 * Separate printer and instance name...
881 */
882
883 if ((instance = strchr(name, '/')) != NULL)
884 *instance++ = '\0';
885
886 /*
887 * Lookup the printer and instance and make it the default...
888 */
889
890 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
891 dest->is_default = 1;
892 }
893 else
894 instance = NULL;
895
896 /*
897 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
898 */
899
900 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
901 num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL,
902 num_dests, dests);
903
904 if ((home = getenv("HOME")) != NULL)
905 {
906 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
907
908 num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL,
909 num_dests, dests);
910 }
911
912 /*
913 * Validate the current default destination - this prevents old
914 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
915 * pointing to a non-existent printer or class...
916 */
917
918 if (num_reals)
919 {
920 /*
921 * See if we have a default printer...
922 */
923
924 if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
925 {
926 /*
927 * Have a default; see if it is real...
928 */
929
930 dest = cupsGetDest(dest->name, NULL, num_reals, reals);
931 }
932
933 /*
934 * If dest is NULL, then no default (that exists) is set, so we
935 * need to set a default if one exists...
936 */
937
938 if (dest == NULL && defprinter != NULL)
939 {
940 for (i = 0; i < num_dests; i ++)
941 (*dests)[i].is_default = 0;
942
943 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
944 dest->is_default = 1;
945 }
946
947 /*
948 * Free memory...
949 */
950
951 free(reals);
952 }
953
954 /*
955 * Return the number of destinations...
956 */
957
958 if (num_dests > 0)
959 _cupsSetError(IPP_OK, NULL, 0);
960
961 return (num_dests);
962 }
963
964
965 /*
966 * 'cupsGetNamedDest()' - Get options for the named destination.
967 *
968 * This function is optimized for retrieving a single destination and should
969 * be used instead of @link cupsGetDests@ and @link cupsGetDest@ when you either
970 * know the name of the destination or want to print to the default destination.
971 * If @code NULL@ is returned, the destination does not exist or there is no
972 * default destination.
973 *
974 * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
975 * server will be used.
976 *
977 * If "name" is @code NULL@, the default printer for the current user will be
978 * returned.
979 *
980 * The returned destination must be freed using @link cupsFreeDests@ with a
981 * "num_dests" value of 1.
982 *
983 * @since CUPS 1.4/Mac OS X 10.6@
984 */
985
986 cups_dest_t * /* O - Destination or @code NULL@ */
987 cupsGetNamedDest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
988 const char *name, /* I - Destination name or @code NULL@ for the default destination */
989 const char *instance) /* I - Instance name or @code NULL@ */
990 {
991 cups_dest_t *dest; /* Destination */
992 char filename[1024], /* Path to lpoptions */
993 defname[256]; /* Default printer name */
994 const char *home = getenv("HOME"); /* Home directory */
995 int set_as_default = 0; /* Set returned destination as default */
996 ipp_op_t op = IPP_GET_PRINTER_ATTRIBUTES;
997 /* IPP operation to get server ops */
998 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
999
1000
1001 /*
1002 * If "name" is NULL, find the default destination...
1003 */
1004
1005 if (!name)
1006 {
1007 set_as_default = 1;
1008 name = _cupsUserDefault(defname, sizeof(defname));
1009
1010 if (name)
1011 {
1012 char *ptr; /* Temporary pointer... */
1013
1014 if ((ptr = strchr(defname, '/')) != NULL)
1015 {
1016 *ptr++ = '\0';
1017 instance = ptr;
1018 }
1019 else
1020 instance = NULL;
1021 }
1022 else if (home)
1023 {
1024 /*
1025 * No default in the environment, try the user's lpoptions files...
1026 */
1027
1028 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
1029
1030 name = cups_get_default(filename, defname, sizeof(defname), &instance);
1031 }
1032
1033 if (!name)
1034 {
1035 /*
1036 * Still not there? Try the system lpoptions file...
1037 */
1038
1039 snprintf(filename, sizeof(filename), "%s/lpoptions",
1040 cg->cups_serverroot);
1041 name = cups_get_default(filename, defname, sizeof(defname), &instance);
1042 }
1043
1044 if (!name)
1045 {
1046 /*
1047 * No locally-set default destination, ask the server...
1048 */
1049
1050 op = CUPS_GET_DEFAULT;
1051 }
1052 }
1053
1054 /*
1055 * Get the printer's attributes...
1056 */
1057
1058 if (!_cupsGetDests(http, op, name, &dest))
1059 {
1060 if (op == CUPS_GET_DEFAULT || (name && !set_as_default))
1061 return (NULL);
1062
1063 /*
1064 * The default printer from environment variables or from a
1065 * configuration file does not exist. Find out the real default.
1066 */
1067
1068 if (!_cupsGetDests(http, CUPS_GET_DEFAULT, NULL, &dest))
1069 return (NULL);
1070 }
1071
1072 if (instance)
1073 dest->instance = _cupsStrAlloc(instance);
1074
1075 if (set_as_default)
1076 dest->is_default = 1;
1077
1078 /*
1079 * Then add local options...
1080 */
1081
1082 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
1083 cups_get_dests(filename, name, instance, 1, 1, &dest);
1084
1085 if (home)
1086 {
1087 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
1088
1089 cups_get_dests(filename, name, instance, 1, 1, &dest);
1090 }
1091
1092 /*
1093 * Return the result...
1094 */
1095
1096 return (dest);
1097 }
1098
1099
1100 /*
1101 * 'cupsRemoveDest()' - Remove a destination from the destination list.
1102 *
1103 * Removing a destination/instance does not delete the class or printer
1104 * queue, merely the lpoptions for that destination/instance. Use the
1105 * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
1106 * options for the user.
1107 *
1108 * @since CUPS 1.3/Mac OS X 10.5@
1109 */
1110
1111 int /* O - New number of destinations */
1112 cupsRemoveDest(const char *name, /* I - Destination name */
1113 const char *instance, /* I - Instance name or @code NULL@ */
1114 int num_dests, /* I - Number of destinations */
1115 cups_dest_t **dests) /* IO - Destinations */
1116 {
1117 int i; /* Index into destinations */
1118 cups_dest_t *dest; /* Pointer to destination */
1119
1120
1121 /*
1122 * Find the destination...
1123 */
1124
1125 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
1126 return (num_dests);
1127
1128 /*
1129 * Free memory...
1130 */
1131
1132 _cupsStrFree(dest->name);
1133 _cupsStrFree(dest->instance);
1134 cupsFreeOptions(dest->num_options, dest->options);
1135
1136 /*
1137 * Remove the destination from the array...
1138 */
1139
1140 num_dests --;
1141
1142 i = dest - *dests;
1143
1144 if (i < num_dests)
1145 memmove(dest, dest + 1, (num_dests - i) * sizeof(cups_dest_t));
1146
1147 return (num_dests);
1148 }
1149
1150
1151 /*
1152 * 'cupsSetDefaultDest()' - Set the default destination.
1153 *
1154 * @since CUPS 1.3/Mac OS X 10.5@
1155 */
1156
1157 void
1158 cupsSetDefaultDest(
1159 const char *name, /* I - Destination name */
1160 const char *instance, /* I - Instance name or @code NULL@ */
1161 int num_dests, /* I - Number of destinations */
1162 cups_dest_t *dests) /* I - Destinations */
1163 {
1164 int i; /* Looping var */
1165 cups_dest_t *dest; /* Current destination */
1166
1167
1168 /*
1169 * Range check input...
1170 */
1171
1172 if (!name || num_dests <= 0 || !dests)
1173 return;
1174
1175 /*
1176 * Loop through the array and set the "is_default" flag for the matching
1177 * destination...
1178 */
1179
1180 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
1181 dest->is_default = !_cups_strcasecmp(name, dest->name) &&
1182 ((!instance && !dest->instance) ||
1183 (instance && dest->instance &&
1184 !_cups_strcasecmp(instance, dest->instance)));
1185 }
1186
1187
1188 /*
1189 * 'cupsSetDests()' - Save the list of destinations for the default server.
1190 *
1191 * This function saves the destinations to /etc/cups/lpoptions when run
1192 * as root and ~/.cups/lpoptions when run as a normal user.
1193 */
1194
1195 void
1196 cupsSetDests(int num_dests, /* I - Number of destinations */
1197 cups_dest_t *dests) /* I - Destinations */
1198 {
1199 cupsSetDests2(CUPS_HTTP_DEFAULT, num_dests, dests);
1200 }
1201
1202
1203 /*
1204 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
1205 *
1206 * This function saves the destinations to /etc/cups/lpoptions when run
1207 * as root and ~/.cups/lpoptions when run as a normal user.
1208 *
1209 * @since CUPS 1.1.21/Mac OS X 10.4@
1210 */
1211
1212 int /* O - 0 on success, -1 on error */
1213 cupsSetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1214 int num_dests, /* I - Number of destinations */
1215 cups_dest_t *dests) /* I - Destinations */
1216 {
1217 int i, j; /* Looping vars */
1218 int wrote; /* Wrote definition? */
1219 cups_dest_t *dest; /* Current destination */
1220 cups_option_t *option; /* Current option */
1221 _ipp_option_t *match; /* Matching attribute for option */
1222 FILE *fp; /* File pointer */
1223 #ifndef WIN32
1224 const char *home; /* HOME environment variable */
1225 #endif /* WIN32 */
1226 char filename[1024]; /* lpoptions file */
1227 int num_temps; /* Number of temporary destinations */
1228 cups_dest_t *temps, /* Temporary destinations */
1229 *temp; /* Current temporary dest */
1230 const char *val; /* Value of temporary option */
1231 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
1232
1233
1234 /*
1235 * Range check the input...
1236 */
1237
1238 if (!num_dests || !dests)
1239 return (-1);
1240
1241 /*
1242 * Get the server destinations...
1243 */
1244
1245 num_temps = _cupsGetDests(http, CUPS_GET_PRINTERS, NULL, &temps);
1246
1247 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE)
1248 {
1249 cupsFreeDests(num_temps, temps);
1250 return (-1);
1251 }
1252
1253 /*
1254 * Figure out which file to write to...
1255 */
1256
1257 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
1258
1259 #ifndef WIN32
1260 if (getuid())
1261 {
1262 /*
1263 * Merge in server defaults...
1264 */
1265
1266 num_temps = cups_get_dests(filename, NULL, NULL, 0, num_temps, &temps);
1267
1268 /*
1269 * Point to user defaults...
1270 */
1271
1272 if ((home = getenv("HOME")) != NULL)
1273 {
1274 /*
1275 * Create ~/.cups subdirectory...
1276 */
1277
1278 snprintf(filename, sizeof(filename), "%s/.cups", home);
1279 if (access(filename, 0))
1280 mkdir(filename, 0700);
1281
1282 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
1283 }
1284 }
1285 #endif /* !WIN32 */
1286
1287 /*
1288 * Try to open the file...
1289 */
1290
1291 if ((fp = fopen(filename, "w")) == NULL)
1292 {
1293 cupsFreeDests(num_temps, temps);
1294 return (-1);
1295 }
1296
1297 #ifndef WIN32
1298 /*
1299 * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
1300 * file...
1301 */
1302
1303 if (!getuid())
1304 fchmod(fileno(fp), 0644);
1305 #endif /* !WIN32 */
1306
1307 /*
1308 * Write each printer; each line looks like:
1309 *
1310 * Dest name[/instance] options
1311 * Default name[/instance] options
1312 */
1313
1314 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
1315 if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
1316 {
1317 if (dest->is_default)
1318 {
1319 fprintf(fp, "Default %s", dest->name);
1320 if (dest->instance)
1321 fprintf(fp, "/%s", dest->instance);
1322
1323 wrote = 1;
1324 }
1325 else
1326 wrote = 0;
1327
1328 if ((temp = cupsGetDest(dest->name, dest->instance, num_temps, temps)) == NULL)
1329 temp = cupsGetDest(dest->name, NULL, num_temps, temps);
1330
1331 for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
1332 {
1333 /*
1334 * See if this option is a printer attribute; if so, skip it...
1335 */
1336
1337 if ((match = _ippFindOption(option->name)) != NULL &&
1338 match->group_tag == IPP_TAG_PRINTER)
1339 continue;
1340
1341 /*
1342 * See if the server/global options match these; if so, don't
1343 * write 'em.
1344 */
1345
1346 if (temp &&
1347 (val = cupsGetOption(option->name, temp->num_options,
1348 temp->options)) != NULL &&
1349 !_cups_strcasecmp(val, option->value))
1350 continue;
1351
1352 /*
1353 * Options don't match, write to the file...
1354 */
1355
1356 if (!wrote)
1357 {
1358 fprintf(fp, "Dest %s", dest->name);
1359 if (dest->instance)
1360 fprintf(fp, "/%s", dest->instance);
1361 wrote = 1;
1362 }
1363
1364 if (option->value[0])
1365 {
1366 if (strchr(option->value, ' ') ||
1367 strchr(option->value, '\\') ||
1368 strchr(option->value, '\"') ||
1369 strchr(option->value, '\''))
1370 {
1371 /*
1372 * Quote the value...
1373 */
1374
1375 fprintf(fp, " %s=\"", option->name);
1376
1377 for (val = option->value; *val; val ++)
1378 {
1379 if (strchr("\"\'\\", *val))
1380 putc('\\', fp);
1381
1382 putc(*val, fp);
1383 }
1384
1385 putc('\"', fp);
1386 }
1387 else
1388 {
1389 /*
1390 * Store the literal value...
1391 */
1392
1393 fprintf(fp, " %s=%s", option->name, option->value);
1394 }
1395 }
1396 else
1397 fprintf(fp, " %s", option->name);
1398 }
1399
1400 if (wrote)
1401 fputs("\n", fp);
1402 }
1403
1404 /*
1405 * Free the temporary destinations and close the file...
1406 */
1407
1408 cupsFreeDests(num_temps, temps);
1409
1410 fclose(fp);
1411
1412 #ifdef __APPLE__
1413 /*
1414 * Set the default printer for this location - this allows command-line
1415 * and GUI applications to share the same default destination...
1416 */
1417
1418 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
1419 {
1420 CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault,
1421 dest->name,
1422 kCFStringEncodingUTF8);
1423 /* Default printer name */
1424
1425 if (name)
1426 {
1427 _cupsAppleSetDefaultPrinter(name);
1428 CFRelease(name);
1429 }
1430 }
1431 #endif /* __APPLE__ */
1432
1433 #ifdef HAVE_NOTIFY_POST
1434 /*
1435 * Send a notification so that MacOS X applications can know about the
1436 * change, too.
1437 */
1438
1439 notify_post("com.apple.printerListChange");
1440 #endif /* HAVE_NOTIFY_POST */
1441
1442 return (0);
1443 }
1444
1445
1446 /*
1447 * '_cupsUserDefault()' - Get the user default printer from environment
1448 * variables and location information.
1449 */
1450
1451 char * /* O - Default printer or NULL */
1452 _cupsUserDefault(char *name, /* I - Name buffer */
1453 size_t namesize) /* I - Size of name buffer */
1454 {
1455 const char *env; /* LPDEST or PRINTER env variable */
1456 #ifdef __APPLE__
1457 CFStringRef locprinter; /* Last printer as this location */
1458 #endif /* __APPLE__ */
1459
1460
1461 if ((env = getenv("LPDEST")) == NULL)
1462 if ((env = getenv("PRINTER")) != NULL && !strcmp(env, "lp"))
1463 env = NULL;
1464
1465 if (env)
1466 {
1467 strlcpy(name, env, namesize);
1468 return (name);
1469 }
1470
1471 #ifdef __APPLE__
1472 /*
1473 * Use location-based defaults if "use last printer" is selected in the
1474 * system preferences...
1475 */
1476
1477 if ((locprinter = _cupsAppleCopyDefaultPrinter()) != NULL)
1478 {
1479 CFStringGetCString(locprinter, name, namesize, kCFStringEncodingUTF8);
1480 CFRelease(locprinter);
1481 }
1482 else
1483 name[0] = '\0';
1484
1485 DEBUG_printf(("1_cupsUserDefault: Returning \"%s\".", name));
1486
1487 return (*name ? name : NULL);
1488
1489 #else
1490 /*
1491 * No location-based defaults on this platform...
1492 */
1493
1494 name[0] = '\0';
1495 return (NULL);
1496 #endif /* __APPLE__ */
1497 }
1498
1499
1500 #ifdef __APPLE__
1501 /*
1502 * 'appleCopyLocations()' - Copy the location history array.
1503 */
1504
1505 static CFArrayRef /* O - Location array or NULL */
1506 appleCopyLocations(void)
1507 {
1508 CFArrayRef locations; /* Location array */
1509
1510
1511 /*
1512 * Look up the location array in the preferences...
1513 */
1514
1515 if ((locations = CFPreferencesCopyAppValue(kLastUsedPrintersKey,
1516 kCUPSPrintingPrefs)) == NULL)
1517 return (NULL);
1518
1519 if (CFGetTypeID(locations) != CFArrayGetTypeID())
1520 {
1521 CFRelease(locations);
1522 return (NULL);
1523 }
1524
1525 return (locations);
1526 }
1527
1528
1529 /*
1530 * 'appleCopyNetwork()' - Get the network ID for the current location.
1531 */
1532
1533 static CFStringRef /* O - Network ID */
1534 appleCopyNetwork(void)
1535 {
1536 SCDynamicStoreRef dynamicStore; /* System configuration data */
1537 CFStringRef key; /* Current network configuration key */
1538 CFDictionaryRef ip_dict; /* Network configuration data */
1539 CFStringRef network = NULL; /* Current network ID */
1540
1541
1542 if ((dynamicStore = SCDynamicStoreCreate(NULL, CFSTR("libcups"), NULL,
1543 NULL)) != NULL)
1544 {
1545 /*
1546 * First use the IPv6 router address, if available, since that will generally
1547 * be a globally-unique link-local address.
1548 */
1549
1550 if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
1551 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6)) != NULL)
1552 {
1553 if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
1554 {
1555 if ((network = CFDictionaryGetValue(ip_dict,
1556 kSCPropNetIPv6Router)) != NULL)
1557 CFRetain(network);
1558
1559 CFRelease(ip_dict);
1560 }
1561
1562 CFRelease(key);
1563 }
1564
1565 /*
1566 * If that doesn't work, try the IPv4 router address. This isn't as unique
1567 * and will likely be a 10.x.y.z or 192.168.y.z address...
1568 */
1569
1570 if (!network)
1571 {
1572 if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
1573 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)) != NULL)
1574 {
1575 if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
1576 {
1577 if ((network = CFDictionaryGetValue(ip_dict,
1578 kSCPropNetIPv4Router)) != NULL)
1579 CFRetain(network);
1580
1581 CFRelease(ip_dict);
1582 }
1583
1584 CFRelease(key);
1585 }
1586 }
1587
1588 CFRelease(dynamicStore);
1589 }
1590
1591 return (network);
1592 }
1593
1594
1595 /*
1596 * 'appleGetPaperSize()' - Get the default paper size.
1597 */
1598
1599 char * /* O - Default paper size */
1600 appleGetPaperSize(char *name, /* I - Paper size name buffer */
1601 int namesize) /* I - Size of buffer */
1602 {
1603 CFStringRef defaultPaperID; /* Default paper ID */
1604 _pwg_media_t *pwgmedia; /* PWG media size */
1605
1606
1607 defaultPaperID = _cupsAppleCopyDefaultPaperID();
1608 if (!defaultPaperID ||
1609 CFGetTypeID(defaultPaperID) != CFStringGetTypeID() ||
1610 !CFStringGetCString(defaultPaperID, name, namesize,
1611 kCFStringEncodingUTF8))
1612 name[0] = '\0';
1613 else if ((pwgmedia = _pwgMediaForLegacy(name)) != NULL)
1614 strlcpy(name, pwgmedia->pwg, namesize);
1615
1616 if (defaultPaperID)
1617 CFRelease(defaultPaperID);
1618
1619 return (name);
1620 }
1621
1622
1623 /*
1624 * 'appleGetPrinter()' - Get a printer from the history array.
1625 */
1626
1627 static CFStringRef /* O - Printer name or NULL */
1628 appleGetPrinter(CFArrayRef locations, /* I - Location array */
1629 CFStringRef network, /* I - Network name */
1630 CFIndex *locindex) /* O - Index in array */
1631 {
1632 CFIndex i, /* Looping var */
1633 count; /* Number of locations */
1634 CFDictionaryRef location; /* Current location */
1635 CFStringRef locnetwork, /* Current network */
1636 locprinter; /* Current printer */
1637
1638
1639 for (i = 0, count = CFArrayGetCount(locations); i < count; i ++)
1640 if ((location = CFArrayGetValueAtIndex(locations, i)) != NULL &&
1641 CFGetTypeID(location) == CFDictionaryGetTypeID())
1642 {
1643 if ((locnetwork = CFDictionaryGetValue(location,
1644 kLocationNetworkKey)) != NULL &&
1645 CFGetTypeID(locnetwork) == CFStringGetTypeID() &&
1646 CFStringCompare(network, locnetwork, 0) == kCFCompareEqualTo &&
1647 (locprinter = CFDictionaryGetValue(location,
1648 kLocationPrinterIDKey)) != NULL &&
1649 CFGetTypeID(locprinter) == CFStringGetTypeID())
1650 {
1651 if (locindex)
1652 *locindex = i;
1653
1654 return (locprinter);
1655 }
1656 }
1657
1658 return (NULL);
1659 }
1660 #endif /* __APPLE__ */
1661
1662
1663 /*
1664 * 'cups_add_dest()' - Add a destination to the array.
1665 *
1666 * Unlike cupsAddDest(), this function does not check for duplicates.
1667 */
1668
1669 static cups_dest_t * /* O - New destination */
1670 cups_add_dest(const char *name, /* I - Name of destination */
1671 const char *instance, /* I - Instance or NULL */
1672 int *num_dests, /* IO - Number of destinations */
1673 cups_dest_t **dests) /* IO - Destinations */
1674 {
1675 int insert, /* Insertion point */
1676 diff; /* Result of comparison */
1677 cups_dest_t *dest; /* Destination pointer */
1678
1679
1680 /*
1681 * Add new destination...
1682 */
1683
1684 if (*num_dests == 0)
1685 dest = malloc(sizeof(cups_dest_t));
1686 else
1687 dest = realloc(*dests, sizeof(cups_dest_t) * (*num_dests + 1));
1688
1689 if (!dest)
1690 return (NULL);
1691
1692 *dests = dest;
1693
1694 /*
1695 * Find where to insert the destination...
1696 */
1697
1698 if (*num_dests == 0)
1699 insert = 0;
1700 else
1701 {
1702 insert = cups_find_dest(name, instance, *num_dests, *dests, *num_dests - 1,
1703 &diff);
1704
1705 if (diff > 0)
1706 insert ++;
1707 }
1708
1709 /*
1710 * Move the array elements as needed...
1711 */
1712
1713 if (insert < *num_dests)
1714 memmove(*dests + insert + 1, *dests + insert,
1715 (*num_dests - insert) * sizeof(cups_dest_t));
1716
1717 (*num_dests) ++;
1718
1719 /*
1720 * Initialize the destination...
1721 */
1722
1723 dest = *dests + insert;
1724 dest->name = _cupsStrAlloc(name);
1725 dest->instance = _cupsStrAlloc(instance);
1726 dest->is_default = 0;
1727 dest->num_options = 0;
1728 dest->options = (cups_option_t *)0;
1729
1730 return (dest);
1731 }
1732
1733
1734 /*
1735 * 'cups_compare_dests()' - Compare two destinations.
1736 */
1737
1738 static int /* O - Result of comparison */
1739 cups_compare_dests(cups_dest_t *a, /* I - First destination */
1740 cups_dest_t *b) /* I - Second destination */
1741 {
1742 int diff; /* Difference */
1743
1744
1745 if ((diff = _cups_strcasecmp(a->name, b->name)) != 0)
1746 return (diff);
1747 else if (a->instance && b->instance)
1748 return (_cups_strcasecmp(a->instance, b->instance));
1749 else
1750 return ((a->instance && !b->instance) - (!a->instance && b->instance));
1751 }
1752
1753
1754 /*
1755 * 'cups_find_dest()' - Find a destination using a binary search.
1756 */
1757
1758 static int /* O - Index of match */
1759 cups_find_dest(const char *name, /* I - Destination name */
1760 const char *instance, /* I - Instance or NULL */
1761 int num_dests, /* I - Number of destinations */
1762 cups_dest_t *dests, /* I - Destinations */
1763 int prev, /* I - Previous index */
1764 int *rdiff) /* O - Difference of match */
1765 {
1766 int left, /* Low mark for binary search */
1767 right, /* High mark for binary search */
1768 current, /* Current index */
1769 diff; /* Result of comparison */
1770 cups_dest_t key; /* Search key */
1771
1772
1773 key.name = (char *)name;
1774 key.instance = (char *)instance;
1775
1776 if (prev >= 0)
1777 {
1778 /*
1779 * Start search on either side of previous...
1780 */
1781
1782 if ((diff = cups_compare_dests(&key, dests + prev)) == 0 ||
1783 (diff < 0 && prev == 0) ||
1784 (diff > 0 && prev == (num_dests - 1)))
1785 {
1786 *rdiff = diff;
1787 return (prev);
1788 }
1789 else if (diff < 0)
1790 {
1791 /*
1792 * Start with previous on right side...
1793 */
1794
1795 left = 0;
1796 right = prev;
1797 }
1798 else
1799 {
1800 /*
1801 * Start wih previous on left side...
1802 */
1803
1804 left = prev;
1805 right = num_dests - 1;
1806 }
1807 }
1808 else
1809 {
1810 /*
1811 * Start search in the middle...
1812 */
1813
1814 left = 0;
1815 right = num_dests - 1;
1816 }
1817
1818 do
1819 {
1820 current = (left + right) / 2;
1821 diff = cups_compare_dests(&key, dests + current);
1822
1823 if (diff == 0)
1824 break;
1825 else if (diff < 0)
1826 right = current;
1827 else
1828 left = current;
1829 }
1830 while ((right - left) > 1);
1831
1832 if (diff != 0)
1833 {
1834 /*
1835 * Check the last 1 or 2 elements...
1836 */
1837
1838 if ((diff = cups_compare_dests(&key, dests + left)) <= 0)
1839 current = left;
1840 else
1841 {
1842 diff = cups_compare_dests(&key, dests + right);
1843 current = right;
1844 }
1845 }
1846
1847 /*
1848 * Return the closest destination and the difference...
1849 */
1850
1851 *rdiff = diff;
1852
1853 return (current);
1854 }
1855
1856
1857 /*
1858 * 'cups_get_default()' - Get the default destination from an lpoptions file.
1859 */
1860
1861 static char * /* O - Default destination or NULL */
1862 cups_get_default(const char *filename, /* I - File to read */
1863 char *namebuf, /* I - Name buffer */
1864 size_t namesize, /* I - Size of name buffer */
1865 const char **instance) /* I - Instance */
1866 {
1867 cups_file_t *fp; /* lpoptions file */
1868 char line[8192], /* Line from file */
1869 *value, /* Value for line */
1870 *nameptr; /* Pointer into name */
1871 int linenum; /* Current line */
1872
1873
1874 *namebuf = '\0';
1875
1876 if ((fp = cupsFileOpen(filename, "r")) != NULL)
1877 {
1878 linenum = 0;
1879
1880 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1881 {
1882 if (!_cups_strcasecmp(line, "default") && value)
1883 {
1884 strlcpy(namebuf, value, namesize);
1885
1886 if ((nameptr = strchr(namebuf, ' ')) != NULL)
1887 *nameptr = '\0';
1888 if ((nameptr = strchr(namebuf, '\t')) != NULL)
1889 *nameptr = '\0';
1890
1891 if ((nameptr = strchr(namebuf, '/')) != NULL)
1892 *nameptr++ = '\0';
1893
1894 *instance = nameptr;
1895 break;
1896 }
1897 }
1898
1899 cupsFileClose(fp);
1900 }
1901
1902 return (*namebuf ? namebuf : NULL);
1903 }
1904
1905
1906 /*
1907 * 'cups_get_dests()' - Get destinations from a file.
1908 */
1909
1910 static int /* O - Number of destinations */
1911 cups_get_dests(
1912 const char *filename, /* I - File to read from */
1913 const char *match_name, /* I - Destination name we want */
1914 const char *match_inst, /* I - Instance name we want */
1915 int user_default_set, /* I - User default printer set? */
1916 int num_dests, /* I - Number of destinations */
1917 cups_dest_t **dests) /* IO - Destinations */
1918 {
1919 int i; /* Looping var */
1920 cups_dest_t *dest; /* Current destination */
1921 cups_file_t *fp; /* File pointer */
1922 char line[8192], /* Line from file */
1923 *lineptr, /* Pointer into line */
1924 *name, /* Name of destination/option */
1925 *instance; /* Instance of destination */
1926 int linenum; /* Current line number */
1927
1928
1929 DEBUG_printf(("7cups_get_dests(filename=\"%s\", match_name=\"%s\", "
1930 "match_inst=\"%s\", user_default_set=%d, num_dests=%d, "
1931 "dests=%p)", filename, match_name, match_inst,
1932 user_default_set, num_dests, dests));
1933
1934 /*
1935 * Try to open the file...
1936 */
1937
1938 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1939 return (num_dests);
1940
1941 /*
1942 * Read each printer; each line looks like:
1943 *
1944 * Dest name[/instance] options
1945 * Default name[/instance] options
1946 */
1947
1948 linenum = 0;
1949
1950 while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
1951 {
1952 /*
1953 * See what type of line it is...
1954 */
1955
1956 DEBUG_printf(("9cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"",
1957 linenum, line, lineptr));
1958
1959 if ((_cups_strcasecmp(line, "dest") && _cups_strcasecmp(line, "default")) || !lineptr)
1960 {
1961 DEBUG_puts("9cups_get_dests: Not a dest or default line...");
1962 continue;
1963 }
1964
1965 name = lineptr;
1966
1967 /*
1968 * Search for an instance...
1969 */
1970
1971 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
1972 lineptr ++;
1973
1974 if (*lineptr == '/')
1975 {
1976 /*
1977 * Found an instance...
1978 */
1979
1980 *lineptr++ = '\0';
1981 instance = lineptr;
1982
1983 /*
1984 * Search for an instance...
1985 */
1986
1987 while (!isspace(*lineptr & 255) && *lineptr)
1988 lineptr ++;
1989 }
1990 else
1991 instance = NULL;
1992
1993 if (*lineptr)
1994 *lineptr++ = '\0';
1995
1996 DEBUG_printf(("9cups_get_dests: name=\"%s\", instance=\"%s\"", name,
1997 instance));
1998
1999 /*
2000 * See if the primary instance of the destination exists; if not,
2001 * ignore this entry and move on...
2002 */
2003
2004 if (match_name)
2005 {
2006 if (_cups_strcasecmp(name, match_name) ||
2007 (!instance && match_inst) ||
2008 (instance && !match_inst) ||
2009 (instance && _cups_strcasecmp(instance, match_inst)))
2010 continue;
2011
2012 dest = *dests;
2013 }
2014 else if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
2015 {
2016 DEBUG_puts("9cups_get_dests: Not found!");
2017 continue;
2018 }
2019 else
2020 {
2021 /*
2022 * Add the destination...
2023 */
2024
2025 num_dests = cupsAddDest(name, instance, num_dests, dests);
2026
2027 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
2028 {
2029 /*
2030 * Out of memory!
2031 */
2032
2033 DEBUG_puts("9cups_get_dests: Out of memory!");
2034 break;
2035 }
2036 }
2037
2038 /*
2039 * Add options until we hit the end of the line...
2040 */
2041
2042 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
2043 &(dest->options));
2044
2045 /*
2046 * If we found what we were looking for, stop now...
2047 */
2048
2049 if (match_name)
2050 break;
2051
2052 /*
2053 * Set this as default if needed...
2054 */
2055
2056 if (!user_default_set && !_cups_strcasecmp(line, "default"))
2057 {
2058 DEBUG_puts("9cups_get_dests: Setting as default...");
2059
2060 for (i = 0; i < num_dests; i ++)
2061 (*dests)[i].is_default = 0;
2062
2063 dest->is_default = 1;
2064 }
2065 }
2066
2067 /*
2068 * Close the file and return...
2069 */
2070
2071 cupsFileClose(fp);
2072
2073 return (num_dests);
2074 }
2075
2076
2077 /*
2078 * 'cups_make_string()' - Make a comma-separated string of values from an IPP
2079 * attribute.
2080 */
2081
2082 static char * /* O - New string */
2083 cups_make_string(
2084 ipp_attribute_t *attr, /* I - Attribute to convert */
2085 char *buffer, /* I - Buffer */
2086 size_t bufsize) /* I - Size of buffer */
2087 {
2088 int i; /* Looping var */
2089 char *ptr, /* Pointer into buffer */
2090 *end, /* Pointer to end of buffer */
2091 *valptr; /* Pointer into string attribute */
2092
2093
2094 /*
2095 * Return quickly if we have a single string value...
2096 */
2097
2098 if (attr->num_values == 1 &&
2099 attr->value_tag != IPP_TAG_INTEGER &&
2100 attr->value_tag != IPP_TAG_ENUM &&
2101 attr->value_tag != IPP_TAG_BOOLEAN &&
2102 attr->value_tag != IPP_TAG_RANGE)
2103 return (attr->values[0].string.text);
2104
2105 /*
2106 * Copy the values to the string, separating with commas and escaping strings
2107 * as needed...
2108 */
2109
2110 end = buffer + bufsize - 1;
2111
2112 for (i = 0, ptr = buffer; i < attr->num_values && ptr < end; i ++)
2113 {
2114 if (i)
2115 *ptr++ = ',';
2116
2117 switch (attr->value_tag)
2118 {
2119 case IPP_TAG_INTEGER :
2120 case IPP_TAG_ENUM :
2121 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].integer);
2122 break;
2123
2124 case IPP_TAG_BOOLEAN :
2125 if (attr->values[i].boolean)
2126 strlcpy(ptr, "true", end - ptr + 1);
2127 else
2128 strlcpy(ptr, "false", end - ptr + 1);
2129 break;
2130
2131 case IPP_TAG_RANGE :
2132 if (attr->values[i].range.lower == attr->values[i].range.upper)
2133 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].range.lower);
2134 else
2135 snprintf(ptr, end - ptr + 1, "%d-%d", attr->values[i].range.lower,
2136 attr->values[i].range.upper);
2137 break;
2138
2139 default :
2140 for (valptr = attr->values[i].string.text;
2141 *valptr && ptr < end;)
2142 {
2143 if (strchr(" \t\n\\\'\"", *valptr))
2144 {
2145 if (ptr >= (end - 1))
2146 break;
2147
2148 *ptr++ = '\\';
2149 }
2150
2151 *ptr++ = *valptr++;
2152 }
2153
2154 *ptr = '\0';
2155 break;
2156 }
2157
2158 ptr += strlen(ptr);
2159 }
2160
2161 *ptr = '\0';
2162
2163 return (buffer);
2164 }
2165
2166
2167 /*
2168 * End of "$Id: dest.c 9568 2011-02-25 06:13:56Z mike $".
2169 */