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