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 "tcpxcv.h" 26#include <winspool.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 46static TCHAR* 47g_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 65static BOOL 66Is64BitWindows() 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 95CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self; 96 97IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet) 98CPrinterSetupWizardSheet::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 119CPrinterSetupWizardSheet::~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// 141void 142CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer) 143{ 144 check( !printer || ( printer != m_selectedPrinter ) ); 145 146 m_selectedPrinter = printer; 147} 148 149 150OSStatus 151CPrinterSetupWizardSheet::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 192exit: 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 219OSStatus 220CPrinterSetupWizardSheet::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 327exit: 328 329 return err; 330} 331 332 333OSStatus 334CPrinterSetupWizardSheet::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 391exit: 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 407OSStatus 408CPrinterSetupWizardSheet::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 450exit: 451 452 if (hPrinter != NULL) 453 { 454 ClosePrinter(hPrinter); 455 } 456 457 return err; 458} 459 460 461OSStatus 462CPrinterSetupWizardSheet::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 490exit: 491 492 if ( hPrinter != NULL ) 493 { 494 ClosePrinter(hPrinter); 495 } 496 497 return err; 498} 499 500 501OSStatus 502CPrinterSetupWizardSheet::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 519exit: 520 521 return err; 522} 523 524 525OSStatus 526CPrinterSetupWizardSheet::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 701exit: 702 703 if ( printerHandle != NULL ) 704 { 705 ClosePrinter( printerHandle ); 706 printerHandle = NULL; 707 } 708 709 return err; 710} 711 712BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet) 713ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent ) 714ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent ) 715ON_WM_SETCURSOR() 716ON_WM_TIMER() 717END_MESSAGE_MAP() 718 719 720// ------------------------------------------------------ 721// OnCommand 722// 723// Traps when the user hits Finish 724// 725BOOL 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// 744BOOL CPrinterSetupWizardSheet::OnInitDialog() 745{ 746 OSStatus err; 747 748 CPropertySheet::OnInitDialog(); 749 750 err = StartBrowse(); 751 require_noerr( err, exit ); 752 753exit: 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// 791BOOL 792CPrinterSetupWizardSheet::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 809void 810CPrinterSetupWizardSheet::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// 826void 827CPrinterSetupWizardSheet::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 866void 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 884LRESULT 885CPrinterSetupWizardSheet::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 917LRESULT 918CPrinterSetupWizardSheet::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 929unsigned WINAPI 930CPrinterSetupWizardSheet::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 970exit: 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 996void DNSSD_API 997CPrinterSetupWizardSheet::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 1080exit: 1081 1082 return; 1083} 1084 1085 1086void DNSSD_API 1087CPrinterSetupWizardSheet::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 1190exit: 1191 1192 return; 1193} 1194 1195 1196void DNSSD_API 1197CPrinterSetupWizardSheet::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 1280exit: 1281 1282 if ( err && service && ( service->serviceRef != NULL ) ) 1283 { 1284 service->printer->window->StopOperation( service->serviceRef ); 1285 } 1286 1287 return; 1288} 1289 1290 1291Printer* 1292CPrinterSetupWizardSheet::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 1364exit: 1365 1366 return printer; 1367} 1368 1369 1370OSStatus 1371CPrinterSetupWizardSheet::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 1415exit: 1416 1417 return err; 1418} 1419 1420 1421OSStatus 1422CPrinterSetupWizardSheet::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 1458OSStatus 1459CPrinterSetupWizardSheet::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 1476exit: 1477 1478 return err; 1479} 1480 1481 1482void 1483CPrinterSetupWizardSheet::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 1510exit: 1511 1512 return; 1513} 1514 1515 1516OSStatus 1517CPrinterSetupWizardSheet::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 1542exit: 1543 1544 return err; 1545} 1546 1547 1548OSStatus 1549CPrinterSetupWizardSheet::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 1576exit: 1577 1578 return err; 1579} 1580 1581 1582OSStatus 1583CPrinterSetupWizardSheet::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 1601exit: 1602 1603 return err; 1604} 1605 1606 1607OSStatus 1608CPrinterSetupWizardSheet::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 1646exit: 1647 1648 return err; 1649} 1650 1651 1652OSStatus 1653CPrinterSetupWizardSheet::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 1670exit: 1671 1672 return err; 1673} 1674 1675 1676OSStatus 1677CPrinterSetupWizardSheet::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 1688exit: 1689 1690 return err; 1691} 1692 1693 1694OSStatus 1695CPrinterSetupWizardSheet::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 1704exit: 1705 1706 return err; 1707} 1708 1709 1710OSStatus 1711CPrinterSetupWizardSheet::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 1729exit: 1730 1731 return err; 1732} 1733 1734 1735OSStatus 1736CPrinterSetupWizardSheet::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 1864exit: 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 1881Printer* 1882CPrinterSetupWizardSheet::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 1902bool 1903CPrinterSetupWizardSheet::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 1942bool 1943CPrinterSetupWizardSheet::OrderQueueFunc( const Queue * q1, const Queue * q2 ) 1944{ 1945 return ( q1->priority <= q2->priority ) ? true : false; 1946} 1947 1948 1949 1950