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