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