]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
mDNSResponder-171.4.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / PrinterSetupWizardSheet.cpp
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16
17 Change History (most recent first):
18
19 $Log: PrinterSetupWizardSheet.cpp,v $
20 Revision 1.35 2006/08/14 23:24:09 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
22
23 Revision 1.34 2005/10/05 17:32:51 herscher
24 <rdar://problem/4141221> Use a case insensitive compare operation to check whether a printer with the same name has already been installed.
25
26 Revision 1.33 2005/07/11 20:17:15 shersche
27 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
28
29 Revision 1.32 2005/07/07 17:53:20 shersche
30 Fix problems associated with the CUPS printer workaround fix.
31
32 Revision 1.31 2005/06/30 18:02:54 shersche
33 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
34
35 Revision 1.30 2005/04/13 17:46:22 shersche
36 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
37
38 Revision 1.29 2005/02/14 20:48:37 shersche
39 <rdar://problem/4003710> Default pdl key to "application/postscript"
40
41 Revision 1.28 2005/02/14 20:37:53 shersche
42 <rdar://problem/4003944> Populate comment field with the model name that users see in the wizard UI.
43
44 Revision 1.27 2005/02/09 05:04:03 shersche
45 <rdar://problem/3946587> Use TXTRecordGetValuePtr() API in ParseTextRecord
46
47 Revision 1.26 2005/02/08 21:45:06 shersche
48 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
49
50 Revision 1.25 2005/02/08 18:54:17 shersche
51 <rdar://problem/3987680> Default queue name is "lp" when rp key is not specified.
52
53 Revision 1.24 2005/02/01 02:15:55 shersche
54 <rdar://problem/3946587> Use TXTRecord parsing APIs in ParseTextRecord
55
56 Revision 1.23 2005/01/31 23:54:30 shersche
57 <rdar://problem/3947508> Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object.
58
59 Revision 1.22 2005/01/25 18:49:43 shersche
60 Get icon resources from resource DLL
61
62 Revision 1.21 2005/01/10 01:09:32 shersche
63 Use the "note" key to populate pLocation field when setting up printer
64
65 Revision 1.20 2005/01/03 19:05:01 shersche
66 Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window
67
68 Revision 1.19 2004/12/31 07:23:53 shersche
69 Don't modify the button setting in SetSelectedPrinter()
70
71 Revision 1.18 2004/12/29 18:53:38 shersche
72 <rdar://problem/3725106>
73 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
74 Bug #: 3725106, 3737413
75
76 Revision 1.17 2004/10/12 18:02:53 shersche
77 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
78 Bug #: 3764873
79
80 Revision 1.16 2004/09/13 21:27:22 shersche
81 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
82 Bug #: 3796483
83
84 Revision 1.15 2004/09/11 05:59:06 shersche
85 <rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
86 Bug #: 3785766
87
88 Revision 1.14 2004/09/02 01:57:58 cheshire
89 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
90
91 Revision 1.13 2004/07/26 21:06:29 shersche
92 <rdar://problem/3739200> Removing trailing '.' in hostname
93 Bug #: 3739200
94
95 Revision 1.12 2004/07/13 21:24:23 rpantos
96 Fix for <rdar://problem/3701120>.
97
98 Revision 1.11 2004/06/28 00:51:47 shersche
99 Move call to EnumPrinters out of browse callback into standalone function
100
101 Revision 1.10 2004/06/27 23:06:47 shersche
102 code cleanup, make sure EnumPrinters returns non-zero value
103
104 Revision 1.9 2004/06/27 15:49:31 shersche
105 clean up some cruft in the printer browsing code
106
107 Revision 1.8 2004/06/27 08:04:51 shersche
108 copy selected printer to prevent printer being deleted out from under
109
110 Revision 1.7 2004/06/26 23:27:12 shersche
111 support for installing multiple printers of the same name
112
113 Revision 1.6 2004/06/26 21:22:39 shersche
114 handle spaces in file names
115
116 Revision 1.5 2004/06/26 03:19:57 shersche
117 clean up warning messages
118
119 Submitted by: herscher
120
121 Revision 1.4 2004/06/25 02:26:52 shersche
122 Normalize key fields in text record entries
123 Submitted by: herscher
124
125 Revision 1.3 2004/06/24 20:12:07 shersche
126 Clean up source code
127 Submitted by: herscher
128
129 Revision 1.2 2004/06/23 17:58:21 shersche
130 <rdar://problem/3701837> eliminated memory leaks on exit
131 <rdar://problem/3701926> installation of a printer that is already installed results in a no-op
132 Bug #: 3701837, 3701926
133 Submitted by: herscher
134
135 Revision 1.1 2004/06/18 04:36:57 rpantos
136 First checked in
137
138
139 */
140
141 #include "stdafx.h"
142 #include "PrinterSetupWizardApp.h"
143 #include "PrinterSetupWizardSheet.h"
144 #include "CommonServices.h"
145 #include "DebugServices.h"
146 #include "WinServices.h"
147 #include "About.h"
148 #include <winspool.h>
149 #include <tcpxcv.h>
150 #include <string>
151
152 // unreachable code
153 #pragma warning(disable:4702)
154
155
156 #if( !TARGET_OS_WINDOWS_CE )
157 # include <mswsock.h>
158 # include <process.h>
159 #endif
160
161 // Private Messages
162
163 #define WM_SOCKET_EVENT ( WM_USER + 0x100 )
164 #define WM_PROCESS_EVENT ( WM_USER + 0x101 )
165
166
167 // CPrinterSetupWizardSheet
168 CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
169
170 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
171 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
172 :CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
173 m_selectedPrinter(NULL),
174 m_driverThreadExitCode( 0 ),
175 m_driverThreadFinished( false ),
176 m_pdlBrowser( NULL ),
177 m_ippBrowser( NULL ),
178 m_lprBrowser( NULL ),
179 m_lastPage( NULL )
180 {
181 m_arrow = LoadCursor(0, IDC_ARROW);
182 m_wait = LoadCursor(0, IDC_APPSTARTING);
183 m_active = m_arrow;
184 m_self = this;
185
186 Init();
187
188 LoadPrinterNames();
189 }
190
191
192 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
193 {
194 Printer * printer;
195
196 while ( m_printers.size() > 0 )
197 {
198 printer = m_printers.front();
199 m_printers.pop_front();
200
201 delete printer;
202 }
203
204 m_self = NULL;
205 }
206
207
208 // ------------------------------------------------------
209 // SetSelectedPrinter
210 //
211 // Manages setting a printer as the printer to install. Stops
212 // any pending resolves.
213 //
214 void
215 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
216 {
217 check( !printer || ( printer != m_selectedPrinter ) );
218
219 m_selectedPrinter = printer;
220 }
221
222
223 OSStatus
224 CPrinterSetupWizardSheet::LoadPrinterNames()
225 {
226 PBYTE buffer = NULL;
227 OSStatus err = 0;
228
229 //
230 // rdar://problem/3701926 - Printer can't be installed twice
231 //
232 // First thing we want to do is make sure the printer isn't already installed.
233 // If the printer name is found, we'll try and rename it until we
234 // find a unique name
235 //
236 DWORD dwNeeded = 0, dwNumPrinters = 0;
237
238 BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
239 err = translate_errno( ok, errno_compat(), kUnknownErr );
240
241 if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
242 {
243 try
244 {
245 buffer = new unsigned char[dwNeeded];
246 }
247 catch (...)
248 {
249 buffer = NULL;
250 }
251
252 require_action( buffer, exit, kNoMemoryErr );
253 ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
254 err = translate_errno( ok, errno_compat(), kUnknownErr );
255 require_noerr( err, exit );
256
257 for (DWORD index = 0; index < dwNumPrinters; index++)
258 {
259 PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
260
261 m_printerNames.push_back( lppi4->pPrinterName );
262 }
263 }
264
265 exit:
266
267 if (buffer != NULL)
268 {
269 delete [] buffer;
270 }
271
272 return err;
273 }
274
275
276
277 // ------------------------------------------------------
278 // InstallPrinter
279 //
280 // Installs a printer with Windows.
281 //
282 // NOTE: this works one of two ways, depending on whether
283 // there are drivers already installed for this printer.
284 // If there are, then we can just create a port with XcvData,
285 // and then call AddPrinter. If not, we use the printui.dll
286 // to install the printer. Actually installing drivers that
287 // are not currently installed is painful, and it's much
288 // easier and less error prone to just let printui.dll do
289 // the hard work for us.
290 //
291
292 OSStatus
293 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
294 {
295 Service * service;
296 BOOL ok;
297 OSStatus err;
298
299 service = printer->services.front();
300 check( service );
301
302 //
303 // if the driver isn't installed, then install it
304 //
305
306 if ( !printer->driverInstalled )
307 {
308 DWORD dwResult;
309 HANDLE hThread;
310 unsigned threadID;
311
312 m_driverThreadFinished = false;
313
314 //
315 // create the thread
316 //
317 hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallDriverThread, printer, 0, &threadID );
318 err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
319 require_noerr( err, exit );
320
321 //
322 // go modal
323 //
324 while (!m_driverThreadFinished)
325 {
326 MSG msg;
327
328 GetMessage( &msg, m_hWnd, 0, 0 );
329 TranslateMessage(&msg);
330 DispatchMessage(&msg);
331 }
332
333 //
334 // Wait until child process exits.
335 //
336 dwResult = WaitForSingleObject( hThread, INFINITE );
337 err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
338 require_noerr( err, exit );
339
340 //
341 // check the return value of thread
342 //
343 require_noerr( m_driverThreadExitCode, exit );
344
345 //
346 // now we know that the driver was successfully installed
347 //
348 printer->driverInstalled = true;
349 }
350
351 if ( service->type == kPDLServiceType )
352 {
353 err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_RAWTCP_TYPE );
354 require_noerr( err, exit );
355 }
356 else if ( service->type == kLPRServiceType )
357 {
358 err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_LPR_TYPE );
359 require_noerr( err, exit );
360 }
361 else if ( service->type == kIPPServiceType )
362 {
363 err = InstallPrinterIPP( printer, service );
364 require_noerr( err, exit );
365 }
366 else
367 {
368 err = kUnknownErr;
369 require_noerr( err, exit );
370 }
371
372 printer->installed = true;
373
374 //
375 // if the user specified a default printer, set it
376 //
377 if (printer->deflt)
378 {
379 ok = SetDefaultPrinter( printer->actualName );
380 err = translate_errno( ok, errno_compat(), err = kUnknownErr );
381 require_noerr( err, exit );
382 }
383
384 exit:
385
386 return err;
387 }
388
389
390 OSStatus
391 CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, DWORD protocol )
392 {
393 PRINTER_DEFAULTS printerDefaults = { NULL, NULL, SERVER_ACCESS_ADMINISTER };
394 DWORD dwStatus;
395 DWORD cbInputData = 100;
396 PBYTE pOutputData = NULL;
397 DWORD cbOutputNeeded = 0;
398 PORT_DATA_1 portData;
399 PRINTER_INFO_2 pInfo;
400 HANDLE hXcv = NULL;
401 HANDLE hPrinter = NULL;
402 Queue * q;
403 BOOL ok;
404 OSStatus err;
405
406 check(printer != NULL);
407 check(printer->installed == false);
408
409 q = service->queues.front();
410 check( q );
411
412 ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
413 err = translate_errno( ok, errno_compat(), kUnknownErr );
414 require_noerr( err, exit );
415
416 //
417 // BUGBUG: MSDN said this is not required, but my experience shows it is required
418 //
419 try
420 {
421 pOutputData = new BYTE[cbInputData];
422 }
423 catch (...)
424 {
425 pOutputData = NULL;
426 }
427
428 require_action( pOutputData, exit, err = kNoMemoryErr );
429
430 //
431 // setup the port
432 //
433 ZeroMemory(&portData, sizeof(PORT_DATA_1));
434 wcscpy(portData.sztPortName, printer->portName);
435
436 portData.dwPortNumber = service->portNumber;
437 portData.dwVersion = 1;
438
439 portData.dwProtocol = protocol;
440 portData.cbSize = sizeof PORT_DATA_1;
441 portData.dwReserved = 0L;
442
443 wcscpy(portData.sztQueue, q->name);
444 wcscpy(portData.sztIPAddress, service->hostname);
445 wcscpy(portData.sztHostAddress, service->hostname);
446
447 ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus);
448 err = translate_errno( ok, errno_compat(), kUnknownErr );
449 require_noerr( err, exit );
450
451 //
452 // add the printer
453 //
454 ZeroMemory(&pInfo, sizeof(pInfo));
455
456 pInfo.pPrinterName = printer->actualName.GetBuffer();
457 pInfo.pServerName = NULL;
458 pInfo.pShareName = NULL;
459 pInfo.pPortName = printer->portName.GetBuffer();
460 pInfo.pDriverName = printer->modelName.GetBuffer();
461 pInfo.pComment = printer->displayModelName.GetBuffer();
462 pInfo.pLocation = q->location.GetBuffer();
463 pInfo.pDevMode = NULL;
464 pInfo.pDevMode = NULL;
465 pInfo.pSepFile = L"";
466 pInfo.pPrintProcessor = L"winprint";
467 pInfo.pDatatype = L"RAW";
468 pInfo.pParameters = L"";
469 pInfo.pSecurityDescriptor = NULL;
470 pInfo.Attributes = PRINTER_ATTRIBUTE_QUEUED;
471 pInfo.Priority = 0;
472 pInfo.DefaultPriority = 0;
473 pInfo.StartTime = 0;
474 pInfo.UntilTime = 0;
475
476 hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
477 err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
478 require_noerr( err, exit );
479
480 exit:
481
482 if (hPrinter != NULL)
483 {
484 ClosePrinter(hPrinter);
485 }
486
487 if (hXcv != NULL)
488 {
489 ClosePrinter(hXcv);
490 }
491
492 if (pOutputData != NULL)
493 {
494 delete [] pOutputData;
495 }
496
497 return err;
498 }
499
500
501 OSStatus
502 CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service)
503 {
504 DEBUG_UNUSED( service );
505
506 Queue * q = service->SelectedQueue();
507 HANDLE hPrinter = NULL;
508 PRINTER_INFO_2 pInfo;
509 OSStatus err;
510
511 check( q );
512
513 //
514 // add the printer
515 //
516 ZeroMemory(&pInfo, sizeof(PRINTER_INFO_2));
517
518 pInfo.pPrinterName = printer->actualName.GetBuffer();
519 pInfo.pPortName = printer->portName.GetBuffer();
520 pInfo.pDriverName = printer->modelName.GetBuffer();
521 pInfo.pPrintProcessor = L"winprint";
522 pInfo.pLocation = q->location.GetBuffer();
523 pInfo.pComment = printer->displayModelName.GetBuffer();
524 pInfo.Attributes = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
525
526 hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
527 err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
528 require_noerr( err, exit );
529
530 exit:
531
532 if ( hPrinter != NULL )
533 {
534 ClosePrinter(hPrinter);
535 }
536
537 return err;
538 }
539
540
541 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
542 ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent )
543 ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
544 ON_WM_SETCURSOR()
545 ON_WM_TIMER()
546 END_MESSAGE_MAP()
547
548
549 // ------------------------------------------------------
550 // OnCommand
551 //
552 // Traps when the user hits Finish
553 //
554 BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
555 {
556 //
557 // Check if this is OK
558 //
559 if (wParam == ID_WIZFINISH) // If OK is hit...
560 {
561 OnOK();
562 }
563
564 return CPropertySheet::OnCommand(wParam, lParam);
565 }
566
567
568 // ------------------------------------------------------
569 // OnInitDialog
570 //
571 // Initializes this Dialog object.
572 //
573 BOOL CPrinterSetupWizardSheet::OnInitDialog()
574 {
575 OSStatus err;
576
577 CPropertySheet::OnInitDialog();
578
579 err = StartBrowse();
580 require_noerr( err, exit );
581
582 exit:
583
584 if ( err )
585 {
586 StopBrowse();
587
588 if ( err == kDNSServiceErr_Firewall )
589 {
590 CString text, caption;
591
592 text.LoadString( IDS_FIREWALL );
593 caption.LoadString( IDS_FIREWALL_CAPTION );
594
595 MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
596 }
597 else
598 {
599 CPrinterSetupWizardSheet::WizardException exc;
600
601 exc.text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
602 exc.caption.LoadString( IDS_ERROR_CAPTION );
603
604 throw(exc);
605 }
606 }
607
608 return TRUE;
609 }
610
611
612 // ------------------------------------------------------
613 // OnSetCursor
614 //
615 // This is called when Windows wants to know what cursor
616 // to display. So we tell it.
617 //
618 BOOL
619 CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message)
620 {
621 DEBUG_UNUSED(pWnd);
622 DEBUG_UNUSED(nHitTest);
623 DEBUG_UNUSED(message);
624
625 SetCursor(m_active);
626 return TRUE;
627 }
628
629
630 // ------------------------------------------------------
631 // OnContextMenu
632 //
633 // This is not fully implemented yet.
634 //
635
636 void
637 CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos)
638 {
639 DEBUG_UNUSED(pWnd);
640 DEBUG_UNUSED(pos);
641
642 CAbout dlg;
643
644 dlg.DoModal();
645 }
646
647
648 // ------------------------------------------------------
649 // OnOK
650 //
651 // This is called when the user hits the "Finish" button
652 //
653 void
654 CPrinterSetupWizardSheet::OnOK()
655 {
656 check ( m_selectedPrinter != NULL );
657
658 SetWizardButtons( PSWIZB_DISABLEDFINISH );
659
660 if ( InstallPrinter( m_selectedPrinter ) != kNoErr )
661 {
662 CString caption;
663 CString message;
664
665 caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
666 message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
667
668 MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
669 }
670
671 StopBrowse();
672 }
673
674
675 // CPrinterSetupWizardSheet message handlers
676
677 void CPrinterSetupWizardSheet::Init(void)
678 {
679 AddPage(&m_pgFirst);
680 AddPage(&m_pgSecond);
681 AddPage(&m_pgThird);
682 AddPage(&m_pgFourth);
683
684 m_psh.dwFlags &= (~PSH_HASHELP);
685
686 m_psh.dwFlags |= PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER;
687 m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
688 m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON);
689
690 m_psh.hInstance = GetNonLocalizedResources();
691
692 SetWizardMode();
693 }
694
695
696 LONG
697 CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
698 {
699 if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
700 {
701 dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
702 }
703 else
704 {
705 SOCKET sock = (SOCKET) inWParam;
706
707 // iterate thru list
708 ServiceRefList::iterator begin = m_serviceRefList.begin();
709 ServiceRefList::iterator end = m_serviceRefList.end();
710
711 while (begin != end)
712 {
713 DNSServiceRef ref = *begin++;
714
715 check(ref != NULL);
716
717 if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
718 {
719 DNSServiceProcessResult(ref);
720 break;
721 }
722 }
723 }
724
725 return ( 0 );
726 }
727
728
729 LONG
730 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
731 {
732 DEBUG_UNUSED(inLParam);
733
734 m_driverThreadExitCode = (DWORD) inWParam;
735 m_driverThreadFinished = true;
736
737 return 0;
738 }
739
740
741 unsigned WINAPI
742 CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam )
743 {
744 Printer * printer = (Printer*) inParam;
745 DWORD exitCode = 0;
746 DWORD dwResult;
747 OSStatus err;
748 STARTUPINFO si;
749 PROCESS_INFORMATION pi;
750 BOOL ok;
751
752 check( printer );
753 check( m_self );
754
755 //
756 // because we're calling endthreadex(), C++ objects won't be cleaned up
757 // correctly. we'll nest the CString 'command' inside a block so
758 // that it's destructor will be invoked.
759 //
760 {
761 CString command;
762
763 ZeroMemory( &si, sizeof(si) );
764 si.cb = sizeof(si);
765 ZeroMemory( &pi, sizeof(pi) );
766
767 command.Format(L"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR) printer->modelName, (LPCTSTR) printer->infFileName );
768
769 ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
770 err = translate_errno( ok, errno_compat(), kUnknownErr );
771 require_noerr( err, exit );
772
773 dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
774 translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
775 require_noerr( err, exit );
776
777 ok = GetExitCodeProcess( pi.hProcess, &exitCode );
778 err = translate_errno( ok, errno_compat(), kUnknownErr );
779 require_noerr( err, exit );
780 }
781
782 exit:
783
784 //
785 // Close process and thread handles.
786 //
787 if ( pi.hProcess )
788 {
789 CloseHandle( pi.hProcess );
790 }
791
792 if ( pi.hThread )
793 {
794 CloseHandle( pi.hThread );
795 }
796
797 //
798 // alert the main thread
799 //
800 m_self->PostMessage( WM_PROCESS_EVENT, err, exitCode );
801
802 _endthreadex_compat( 0 );
803
804 return 0;
805 }
806
807
808 void DNSSD_API
809 CPrinterSetupWizardSheet::OnBrowse(
810 DNSServiceRef inRef,
811 DNSServiceFlags inFlags,
812 uint32_t inInterfaceIndex,
813 DNSServiceErrorType inErrorCode,
814 const char * inName,
815 const char * inType,
816 const char * inDomain,
817 void * inContext )
818 {
819 DEBUG_UNUSED(inRef);
820
821 CPrinterSetupWizardSheet * self;
822 bool moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
823 CPropertyPage * active;
824 Printer * printer = NULL;
825 Service * service = NULL;
826 OSStatus err = kNoErr;
827
828 require_noerr( inErrorCode, exit );
829
830 self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
831 require_quiet( self, exit );
832
833 active = self->GetActivePage();
834 require_quiet( active, exit );
835
836 // Have we seen this printer before?
837
838 printer = self->Lookup( inName );
839
840 if ( printer )
841 {
842 service = printer->LookupService( inType );
843 }
844
845 if ( inFlags & kDNSServiceFlagsAdd )
846 {
847 if (printer == NULL)
848 {
849 // If not, then create a new one
850
851 printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
852 require_action( printer, exit, err = kUnknownErr );
853 }
854
855 if ( !service )
856 {
857 err = self->OnAddService( printer, inInterfaceIndex, inName, inType, inDomain );
858 require_noerr( err, exit );
859 }
860 else
861 {
862 service->refs++;
863 }
864 }
865 else if ( printer )
866 {
867 check( service );
868
869 err = self->OnRemoveService( service );
870 require_noerr( err, exit );
871
872 if ( printer->services.size() == 0 )
873 {
874 err = self->OnRemovePrinter( printer, moreComing );
875 require_noerr( err, exit );
876 }
877 }
878
879 exit:
880
881 return;
882 }
883
884
885 void DNSSD_API
886 CPrinterSetupWizardSheet::OnResolve(
887 DNSServiceRef inRef,
888 DNSServiceFlags inFlags,
889 uint32_t inInterfaceIndex,
890 DNSServiceErrorType inErrorCode,
891 const char * inFullName,
892 const char * inHostName,
893 uint16_t inPort,
894 uint16_t inTXTSize,
895 const char * inTXT,
896 void * inContext )
897 {
898 DEBUG_UNUSED(inFullName);
899 DEBUG_UNUSED(inInterfaceIndex);
900 DEBUG_UNUSED(inFlags);
901 DEBUG_UNUSED(inRef);
902
903 CPrinterSetupWizardSheet * self;
904 Service * service;
905 Queue * q;
906 int idx;
907 OSStatus err;
908
909 require_noerr( inErrorCode, exit );
910
911 service = reinterpret_cast<Service*>( inContext );
912 require_quiet( service, exit);
913
914 check( service->refs != 0 );
915
916 self = service->printer->window;
917 require_quiet( self, exit );
918
919 err = self->StopOperation( service->serviceRef );
920 require_noerr( err, exit );
921
922 //
923 // hold on to the hostname...
924 //
925 err = UTF8StringToStringObject( inHostName, service->hostname );
926 require_noerr( err, exit );
927
928 //
929 // <rdar://problem/3739200> remove the trailing dot on hostname
930 //
931 idx = service->hostname.ReverseFind('.');
932
933 if ((idx > 1) && ((service->hostname.GetLength() - 1) == idx))
934 {
935 service->hostname.Delete(idx, 1);
936 }
937
938 //
939 // hold on to the port
940 //
941 service->portNumber = ntohs(inPort);
942
943 if ( service->qtotal == 1 )
944 {
945 //
946 // create a new queue
947 //
948 try
949 {
950 q = new Queue;
951 }
952 catch (...)
953 {
954 q = NULL;
955 }
956
957 require_action( q, exit, err = E_OUTOFMEMORY );
958
959 //
960 // parse the text record.
961 //
962
963 err = self->ParseTextRecord( service, q, inTXTSize, inTXT );
964 require_noerr( err, exit );
965
966 service->queues.push_back( q );
967
968 //
969 // we've completely resolved this service
970 //
971
972 self->OnResolveService( service );
973 }
974 else
975 {
976 //
977 // if qtotal is more than 1, then we need to get additional
978 // text records. if not, then this service is considered
979 // resolved
980 //
981
982 err = DNSServiceQueryRecord(&service->serviceRef, 0, inInterfaceIndex, inFullName, kDNSServiceType_TXT, kDNSServiceClass_IN, OnQuery, (void*) service );
983 require_noerr( err, exit );
984
985 err = self->StartOperation( service->serviceRef );
986 require_noerr( err, exit );
987 }
988
989 exit:
990
991 return;
992 }
993
994
995 void DNSSD_API
996 CPrinterSetupWizardSheet::OnQuery(
997 DNSServiceRef inRef,
998 DNSServiceFlags inFlags,
999 uint32_t inInterfaceIndex,
1000 DNSServiceErrorType inErrorCode,
1001 const char * inFullName,
1002 uint16_t inRRType,
1003 uint16_t inRRClass,
1004 uint16_t inRDLen,
1005 const void * inRData,
1006 uint32_t inTTL,
1007 void * inContext)
1008 {
1009 DEBUG_UNUSED( inTTL );
1010 DEBUG_UNUSED( inRRClass );
1011 DEBUG_UNUSED( inRRType );
1012 DEBUG_UNUSED( inFullName );
1013 DEBUG_UNUSED( inInterfaceIndex );
1014 DEBUG_UNUSED( inRef );
1015
1016 Service * service = NULL;
1017 Queue * q;
1018 CPrinterSetupWizardSheet * self;
1019 OSStatus err = kNoErr;
1020
1021 require_noerr( inErrorCode, exit );
1022
1023 service = reinterpret_cast<Service*>( inContext );
1024 require_quiet( service, exit);
1025
1026 self = service->printer->window;
1027 require_quiet( self, exit );
1028
1029 if ( ( inFlags & kDNSServiceFlagsAdd ) && ( inRDLen > 0 ) && ( inRData != NULL ) )
1030 {
1031 const char * inTXT = ( const char * ) inRData;
1032
1033 //
1034 // create a new queue
1035 //
1036 try
1037 {
1038 q = new Queue;
1039 }
1040 catch (...)
1041 {
1042 q = NULL;
1043 }
1044
1045 require_action( q, exit, err = E_OUTOFMEMORY );
1046
1047 err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT );
1048 require_noerr( err, exit );
1049
1050 //
1051 // add this queue
1052 //
1053
1054 service->queues.push_back( q );
1055
1056 if ( service->queues.size() == service->qtotal )
1057 {
1058 //
1059 // else if moreComing is not set, then we're going
1060 // to assume that we're done
1061 //
1062
1063 self->StopOperation( service->serviceRef );
1064
1065 //
1066 // sort the queues
1067 //
1068
1069 service->queues.sort( OrderQueueFunc );
1070
1071 //
1072 // we've completely resolved this service
1073 //
1074
1075 self->OnResolveService( service );
1076 }
1077 }
1078
1079 exit:
1080
1081 if ( err && service && ( service->serviceRef != NULL ) )
1082 {
1083 service->printer->window->StopOperation( service->serviceRef );
1084 }
1085
1086 return;
1087 }
1088
1089
1090 Printer*
1091 CPrinterSetupWizardSheet::OnAddPrinter(
1092 uint32_t inInterfaceIndex,
1093 const char * inName,
1094 const char * inType,
1095 const char * inDomain,
1096 bool moreComing)
1097 {
1098 Printer * printer = NULL;
1099 DWORD printerNameCount;
1100 OSStatus err;
1101
1102 DEBUG_UNUSED( inInterfaceIndex );
1103 DEBUG_UNUSED( inType );
1104 DEBUG_UNUSED( inDomain );
1105
1106 try
1107 {
1108 printer = new Printer;
1109 }
1110 catch (...)
1111 {
1112 printer = NULL;
1113 }
1114
1115 require_action( printer, exit, err = E_OUTOFMEMORY );
1116
1117 printer->window = this;
1118 printer->name = inName;
1119
1120 err = UTF8StringToStringObject(inName, printer->displayName);
1121 check_noerr( err );
1122 printer->actualName = printer->displayName;
1123 printer->installed = false;
1124 printer->deflt = false;
1125 printer->resolving = 0;
1126
1127 // Compare this name against printers that are already installed
1128 // to avoid name clashes. Rename as necessary
1129 // to come up with a unique name.
1130
1131 printerNameCount = 2;
1132
1133 for (;;)
1134 {
1135 CPrinterSetupWizardSheet::PrinterNames::iterator it;
1136
1137 // <rdar://problem/4141221> Don't use find to do comparisons because we need to
1138 // do a case insensitive string comparison
1139
1140 for ( it = m_printerNames.begin(); it != m_printerNames.end(); it++ )
1141 {
1142 if ( (*it).CompareNoCase( printer->actualName ) == 0 )
1143 {
1144 break;
1145 }
1146 }
1147
1148 if (it != m_printerNames.end())
1149 {
1150 printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
1151 }
1152 else
1153 {
1154 break;
1155 }
1156
1157 printerNameCount++;
1158 }
1159
1160 m_printers.push_back( printer );
1161
1162 if ( GetActivePage() == &m_pgSecond )
1163 {
1164 m_pgSecond.OnAddPrinter( printer, moreComing );
1165 }
1166
1167 exit:
1168
1169 return printer;
1170 }
1171
1172
1173 OSStatus
1174 CPrinterSetupWizardSheet::OnAddService(
1175 Printer * printer,
1176 uint32_t inInterfaceIndex,
1177 const char * inName,
1178 const char * inType,
1179 const char * inDomain)
1180 {
1181 Service * service = NULL;
1182 OSStatus err = kNoErr;
1183
1184 DEBUG_UNUSED( inName );
1185 DEBUG_UNUSED( inDomain );
1186
1187 try
1188 {
1189 service = new Service;
1190 }
1191 catch (...)
1192 {
1193 service = NULL;
1194 }
1195
1196 require_action( service, exit, err = E_OUTOFMEMORY );
1197
1198 service->printer = printer;
1199 service->ifi = inInterfaceIndex;
1200 service->type = inType;
1201 service->domain = inDomain;
1202 service->qtotal = 1;
1203 service->refs = 1;
1204 service->serviceRef = NULL;
1205
1206 printer->services.push_back( service );
1207
1208 //
1209 // if the printer is selected, then we'll want to start a
1210 // resolve on this guy
1211 //
1212
1213 if ( printer == m_selectedPrinter )
1214 {
1215 StartResolve( service );
1216 }
1217
1218 exit:
1219
1220 return err;
1221 }
1222
1223
1224 OSStatus
1225 CPrinterSetupWizardSheet::OnRemovePrinter( Printer * printer, bool moreComing )
1226 {
1227 CPropertyPage * active = GetActivePage();
1228 OSStatus err = kNoErr;
1229
1230 if ( active == &m_pgSecond )
1231 {
1232 m_pgSecond.OnRemovePrinter( printer, moreComing );
1233 }
1234
1235 m_printers.remove( printer );
1236
1237 if ( m_selectedPrinter == printer )
1238 {
1239 m_selectedPrinter = NULL;
1240
1241 if ( ( active == &m_pgThird ) || ( active == &m_pgFourth ) )
1242 {
1243 CString caption;
1244 CString message;
1245
1246 caption.LoadString( IDS_ERROR_CAPTION );
1247 message.LoadString( IDS_PRINTER_UNAVAILABLE );
1248
1249 MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
1250
1251 SetActivePage( &m_pgSecond );
1252 }
1253 }
1254
1255 delete printer;
1256
1257 return err;
1258 }
1259
1260
1261 OSStatus
1262 CPrinterSetupWizardSheet::OnRemoveService( Service * service )
1263 {
1264 OSStatus err = kNoErr;
1265
1266 if ( service && ( --service->refs == 0 ) )
1267 {
1268 if ( service->serviceRef != NULL )
1269 {
1270 err = StopResolve( service );
1271 require_noerr( err, exit );
1272 }
1273
1274 service->printer->services.remove( service );
1275
1276 delete service;
1277 }
1278
1279 exit:
1280
1281 return err;
1282 }
1283
1284
1285 void
1286 CPrinterSetupWizardSheet::OnResolveService( Service * service )
1287 {
1288 // Make sure that the active page is page 2
1289
1290 require_quiet( GetActivePage() == &m_pgSecond, exit );
1291
1292 if ( !--service->printer->resolving )
1293 {
1294 // sort the services now. we want the service that
1295 // has the highest priority queue to be first in
1296 // the list.
1297
1298 service->printer->services.sort( OrderServiceFunc );
1299
1300 // Now we can hit next
1301
1302 SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT );
1303
1304 // Reset the cursor
1305
1306 m_active = m_arrow;
1307
1308 // And tell page 2 about it
1309
1310 m_pgSecond.OnResolveService( service );
1311 }
1312
1313 exit:
1314
1315 return;
1316 }
1317
1318
1319 OSStatus
1320 CPrinterSetupWizardSheet::StartBrowse()
1321 {
1322 OSStatus err;
1323
1324 //
1325 // setup the DNS-SD browsing
1326 //
1327 err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLServiceType, NULL, OnBrowse, this );
1328 require_noerr( err, exit );
1329
1330 err = StartOperation( m_pdlBrowser );
1331 require_noerr( err, exit );
1332
1333 err = DNSServiceBrowse( &m_lprBrowser, 0, 0, kLPRServiceType, NULL, OnBrowse, this );
1334 require_noerr( err, exit );
1335
1336 err = StartOperation( m_lprBrowser );
1337 require_noerr( err, exit );
1338
1339 err = DNSServiceBrowse( &m_ippBrowser, 0, 0, kIPPServiceType, NULL, OnBrowse, this );
1340 require_noerr( err, exit );
1341
1342 err = StartOperation( m_ippBrowser );
1343 require_noerr( err, exit );
1344
1345 exit:
1346
1347 return err;
1348 }
1349
1350
1351 OSStatus
1352 CPrinterSetupWizardSheet::StopBrowse()
1353 {
1354 OSStatus err;
1355
1356 err = StopOperation( m_pdlBrowser );
1357 require_noerr( err, exit );
1358
1359 err = StopOperation( m_lprBrowser );
1360 require_noerr( err, exit );
1361
1362 err = StopOperation( m_ippBrowser );
1363 require_noerr( err, exit );
1364
1365 while ( m_printers.size() > 0 )
1366 {
1367 Printer * printer = m_printers.front();
1368
1369 m_printers.pop_front();
1370
1371 if ( printer->resolving )
1372 {
1373 StopResolve( printer );
1374 }
1375
1376 delete printer;
1377 }
1378
1379 exit:
1380
1381 return err;
1382 }
1383
1384
1385 OSStatus
1386 CPrinterSetupWizardSheet::StartResolve( Printer * printer )
1387 {
1388 OSStatus err = kNoErr;
1389 Services::iterator it;
1390
1391 check( printer );
1392
1393 for ( it = printer->services.begin(); it != printer->services.end(); it++ )
1394 {
1395 if ( (*it)->serviceRef == NULL )
1396 {
1397 err = StartResolve( *it );
1398 require_noerr( err, exit );
1399 }
1400 }
1401
1402 m_selectedPrinter = printer;
1403
1404 exit:
1405
1406 return err;
1407 }
1408
1409
1410 OSStatus
1411 CPrinterSetupWizardSheet::StartResolve( Service * service )
1412 {
1413 OSStatus err = kNoErr;
1414
1415 check( service->serviceRef == NULL );
1416
1417 //
1418 // clean out any queues that were collected during a previous
1419 // resolve
1420 //
1421
1422 service->EmptyQueues();
1423
1424 //
1425 // now start the new resolve
1426 //
1427
1428 err = DNSServiceResolve( &service->serviceRef, 0, 0, service->printer->name.c_str(), service->type.c_str(), service->domain.c_str(), (DNSServiceResolveReply) OnResolve, service );
1429 require_noerr( err, exit );
1430
1431 err = StartOperation( service->serviceRef );
1432 require_noerr( err, exit );
1433
1434 //
1435 // If we're not currently resolving, then disable the next button
1436 // and set the cursor to hourglass
1437 //
1438
1439 if ( !service->printer->resolving )
1440 {
1441 SetWizardButtons( PSWIZB_BACK );
1442
1443 m_active = m_wait;
1444 SetCursor(m_active);
1445 }
1446
1447 service->printer->resolving++;
1448
1449 exit:
1450
1451 return err;
1452 }
1453
1454
1455 OSStatus
1456 CPrinterSetupWizardSheet::StopResolve(Printer * printer)
1457 {
1458 OSStatus err = kNoErr;
1459
1460 check( printer );
1461
1462 Services::iterator it;
1463
1464 for ( it = printer->services.begin(); it != printer->services.end(); it++ )
1465 {
1466 if ( (*it)->serviceRef )
1467 {
1468 err = StopResolve( *it );
1469 require_noerr( err, exit );
1470 }
1471 }
1472
1473 exit:
1474
1475 return err;
1476 }
1477
1478
1479 OSStatus
1480 CPrinterSetupWizardSheet::StopResolve( Service * service )
1481 {
1482 OSStatus err;
1483
1484 check( service->serviceRef );
1485
1486 err = StopOperation( service->serviceRef );
1487 require_noerr( err, exit );
1488
1489 service->printer->resolving--;
1490
1491 exit:
1492
1493 return err;
1494 }
1495
1496
1497 OSStatus
1498 CPrinterSetupWizardSheet::StartOperation( DNSServiceRef ref )
1499 {
1500 OSStatus err;
1501
1502 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(ref), m_hWnd, WM_SOCKET_EVENT, FD_READ|FD_CLOSE);
1503 require_noerr( err, exit );
1504
1505 m_serviceRefList.push_back( ref );
1506
1507 exit:
1508
1509 return err;
1510 }
1511
1512
1513 OSStatus
1514 CPrinterSetupWizardSheet::StopOperation( DNSServiceRef & ref )
1515 {
1516 OSStatus err = kNoErr;
1517
1518 if ( ref )
1519 {
1520 m_serviceRefList.remove( ref );
1521
1522 if ( IsWindow( m_hWnd ) )
1523 {
1524 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD( ref ), m_hWnd, 0, 0 );
1525 require_noerr( err, exit );
1526 }
1527
1528 DNSServiceRefDeallocate( ref );
1529 ref = NULL;
1530 }
1531
1532 exit:
1533
1534 return err;
1535 }
1536
1537
1538 OSStatus
1539 CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT )
1540 {
1541 check( service );
1542 check( q );
1543
1544 // <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
1545
1546 bool qtotalDefined = false;
1547 const void * val;
1548 char buf[256];
1549 uint8_t len;
1550 OSStatus err = kNoErr;
1551
1552 // <rdar://problem/3987680> Default to queue "lp"
1553
1554 q->name = L"lp";
1555
1556 // <rdar://problem/4003710> Default pdl key to be "application/postscript"
1557
1558 q->pdl = L"application/postscript";
1559
1560 if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "rp", &len ) ) != NULL )
1561 {
1562 // Stringize val ( doesn't have trailing '\0' yet )
1563
1564 memcpy( buf, val, len );
1565 buf[len] = '\0';
1566
1567 err = UTF8StringToStringObject( buf, q->name );
1568 require_noerr( err, exit );
1569 }
1570
1571 if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "pdl", &len ) ) != NULL )
1572 {
1573 // Stringize val ( doesn't have trailing '\0' yet )
1574
1575 memcpy( buf, val, len );
1576 buf[len] = '\0';
1577
1578 err = UTF8StringToStringObject( buf, q->pdl );
1579 require_noerr( err, exit );
1580 }
1581
1582 if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mfg", &len ) ) != NULL ) ||
1583 ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_manufacturer", &len ) ) != NULL ) )
1584 {
1585 // Stringize val ( doesn't have trailing '\0' yet )
1586
1587 memcpy( buf, val, len );
1588 buf[len] = '\0';
1589
1590 err = UTF8StringToStringObject( buf, q->usb_MFG );
1591 require_noerr( err, exit );
1592 }
1593
1594 if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mdl", &len ) ) != NULL ) ||
1595 ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_model", &len ) ) != NULL ) )
1596 {
1597 // Stringize val ( doesn't have trailing '\0' yet )
1598
1599 memcpy( buf, val, len );
1600 buf[len] = '\0';
1601
1602 err = UTF8StringToStringObject( buf, q->usb_MDL );
1603 require_noerr( err, exit );
1604 }
1605
1606 if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "ty", &len ) ) != NULL )
1607 {
1608 // Stringize val ( doesn't have trailing '\0' yet )
1609
1610 memcpy( buf, val, len );
1611 buf[len] = '\0';
1612
1613 err = UTF8StringToStringObject( buf, q->description );
1614 require_noerr( err, exit );
1615 }
1616
1617 if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "product", &len ) ) != NULL )
1618 {
1619 // Stringize val ( doesn't have trailing '\0' yet )
1620
1621 memcpy( buf, val, len );
1622 buf[len] = '\0';
1623
1624 err = UTF8StringToStringObject( buf, q->product );
1625 require_noerr( err, exit );
1626 }
1627
1628 if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "note", &len ) ) != NULL )
1629 {
1630 // Stringize val ( doesn't have trailing '\0' yet )
1631
1632 memcpy( buf, val, len );
1633 buf[len] = '\0';
1634
1635 err = UTF8StringToStringObject( buf, q->location );
1636 require_noerr( err, exit );
1637 }
1638
1639 if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "qtotal", &len ) ) != NULL )
1640 {
1641 // Stringize val ( doesn't have trailing '\0' yet )
1642
1643 memcpy( buf, val, len );
1644 buf[len] = '\0';
1645
1646 service->qtotal = (unsigned short) atoi( buf );
1647 qtotalDefined = true;
1648 }
1649
1650 if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "priority", &len ) ) != NULL )
1651 {
1652 // Stringize val ( doesn't have trailing '\0' yet )
1653
1654 memcpy( buf, val, len );
1655 buf[len] = '\0';
1656
1657 q->priority = atoi( buf );
1658 }
1659
1660 // <rdar://problem/4124524> Was this printer discovered via OS X Printer Sharing?
1661
1662 if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) )
1663 {
1664 service->printer->isSharedFromOSX = true;
1665 }
1666
1667 exit:
1668
1669 // The following code is to fix a problem with older HP
1670 // printers that don't include "qtotal" in their text
1671 // record. We'll check to see if the q->name is "TEXT"
1672 // and if so, we're going to modify it to be "lp" so
1673 // that we don't use the wrong queue
1674
1675 if ( !err && !qtotalDefined && ( q->name == L"TEXT" ) )
1676 {
1677 q->name = "lp";
1678 }
1679
1680 return err;
1681 }
1682
1683
1684 Printer*
1685 CPrinterSetupWizardSheet::Lookup(const char * inName)
1686 {
1687 check( inName );
1688
1689 Printer * printer = NULL;
1690 Printers::iterator it;
1691
1692 for ( it = m_printers.begin(); it != m_printers.end(); it++ )
1693 {
1694 if ( (*it)->name == inName )
1695 {
1696 printer = *it;
1697 break;
1698 }
1699 }
1700
1701 return printer;
1702 }
1703
1704
1705 bool
1706 CPrinterSetupWizardSheet::OrderServiceFunc( const Service * a, const Service * b )
1707 {
1708 Queue * q1, * q2;
1709
1710 q1 = (a->queues.size() > 0) ? a->queues.front() : NULL;
1711
1712 q2 = (b->queues.size() > 0) ? b->queues.front() : NULL;
1713
1714 if ( !q1 && !q2 )
1715 {
1716 return true;
1717 }
1718 else if ( q1 && !q2 )
1719 {
1720 return true;
1721 }
1722 else if ( !q1 && q2 )
1723 {
1724 return false;
1725 }
1726 else if ( q1->priority < q2->priority )
1727 {
1728 return true;
1729 }
1730 else if ( q1->priority > q2->priority )
1731 {
1732 return false;
1733 }
1734 else if ( ( a->type == kPDLServiceType ) || ( ( a->type == kLPRServiceType ) && ( b->type == kIPPServiceType ) ) )
1735 {
1736 return true;
1737 }
1738 else
1739 {
1740 return false;
1741 }
1742 }
1743
1744
1745 bool
1746 CPrinterSetupWizardSheet::OrderQueueFunc( const Queue * q1, const Queue * q2 )
1747 {
1748 return ( q1->priority <= q2->priority ) ? true : false;
1749 }
1750
1751
1752