1 /* 2 * Copyright 2001-2010, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ithamar R. Adema 7 * Michael Pfeiffer 8 */ 9 #include "PrintServerApp.h" 10 11 #include "BeUtils.h" 12 #include "Printer.h" 13 #include "pr_server.h" 14 #include "Transport.h" 15 16 // BeOS API 17 #include <Alert.h> 18 #include <Autolock.h> 19 #include <Catalog.h> 20 #include <Directory.h> 21 #include <File.h> 22 #include <FindDirectory.h> 23 #include <image.h> 24 #include <Locale.h> 25 #include <Mime.h> 26 #include <NodeInfo.h> 27 #include <NodeMonitor.h> 28 #include <Path.h> 29 #include <Roster.h> 30 #include <PrintJob.h> 31 #include <String.h> 32 33 // ANSI C 34 #include <stdio.h> 35 // for printf 36 #include <unistd.h> 37 // for unlink 38 39 40 #undef B_TRANSLATION_CONTEXT 41 #define B_TRANSLATION_CONTEXT "PrintServerApp" 42 43 44 typedef struct _printer_data { 45 char defaultPrinterName[256]; 46 } printer_data_t; 47 48 49 50 static const char* kSettingsName = "print_server_settings"; 51 52 53 BLocker *gLock = NULL; 54 55 56 /** 57 * Main entry point of print_server. 58 * 59 * @returns B_OK if application was started, or an errorcode if 60 * application failed to start. 61 */ 62 int 63 main() 64 { 65 status_t rc = B_OK; 66 gLock = new BLocker(); 67 PrintServerApp print_server(&rc); 68 if (rc == B_OK) { 69 print_server.Run(); 70 } 71 delete gLock; 72 return rc; 73 } 74 75 76 /** 77 * Constructor for print_server's application class. Retrieves the 78 * name of the default printer from storage, caches the icons for 79 * a selected printer. 80 * 81 * @param err Pointer to status_t for storing result of application 82 * initialisation. 83 * 84 * @see BApplication 85 */ 86 PrintServerApp::PrintServerApp(status_t* err) 87 : Inherited(PSRV_SIGNATURE_TYPE, err), 88 fDefaultPrinter(NULL), 89 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 90 fIconSize(0), 91 fSelectedIcon(NULL), 92 #else 93 fSelectedIconMini(NULL), 94 fSelectedIconLarge(NULL), 95 #endif 96 fReferences(0), 97 fHasReferences(0), 98 fUseConfigWindow(true), 99 fFolder(NULL) 100 { 101 fSettings = Settings::GetSettings(); 102 LoadSettings(); 103 104 if (*err != B_OK) 105 return; 106 107 fHasReferences = create_sem(1, "has_references"); 108 109 // Build list of transport addons 110 Transport::Scan(B_USER_NONPACKAGED_ADDONS_DIRECTORY); 111 Transport::Scan(B_USER_ADDONS_DIRECTORY); 112 Transport::Scan(B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY); 113 Transport::Scan(B_SYSTEM_ADDONS_DIRECTORY); 114 115 SetupPrinterList(); 116 RetrieveDefaultPrinter(); 117 118 // Cache icons for selected printer 119 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 120 BMimeType type(PSRV_PRINTER_FILETYPE); 121 type.GetIcon(&fSelectedIcon, &fIconSize); 122 #else 123 fSelectedIconMini = new BBitmap(BRect(0,0,B_MINI_ICON-1,B_MINI_ICON-1), B_CMAP8); 124 fSelectedIconLarge = new BBitmap(BRect(0,0,B_LARGE_ICON-1,B_LARGE_ICON-1), B_CMAP8); 125 BMimeType type(PRNT_SIGNATURE_TYPE); 126 type.GetIcon(fSelectedIconMini, B_MINI_ICON); 127 type.GetIcon(fSelectedIconLarge, B_LARGE_ICON); 128 #endif 129 130 PostMessage(PSRV_PRINT_SPOOLED_JOB); 131 // Start handling of spooled files 132 } 133 134 135 PrintServerApp::~PrintServerApp() 136 { 137 SaveSettings(); 138 delete fSettings; 139 } 140 141 142 bool 143 PrintServerApp::QuitRequested() 144 { 145 // don't quit when user types Command+Q! 146 BMessage* m = CurrentMessage(); 147 bool shortcut; 148 if (m != NULL && m->FindBool("shortcut", &shortcut) == B_OK && shortcut) 149 return false; 150 151 if (!Inherited::QuitRequested()) 152 return false; 153 154 // Stop watching the folder 155 delete fFolder; fFolder = NULL; 156 157 // Release all printers 158 Printer* printer; 159 while ((printer = Printer::At(0)) != NULL) { 160 printer->AbortPrintThread(); 161 UnregisterPrinter(printer); 162 } 163 164 // Wait for printers 165 if (fHasReferences > 0) { 166 acquire_sem(fHasReferences); 167 delete_sem(fHasReferences); 168 fHasReferences = 0; 169 } 170 171 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 172 delete [] fSelectedIcon; 173 fSelectedIcon = NULL; 174 #else 175 delete fSelectedIconMini; 176 fSelectedIconMini = NULL; 177 delete fSelectedIconLarge; 178 fSelectedIconLarge = NULL; 179 #endif 180 181 return true; 182 } 183 184 185 void 186 PrintServerApp::Acquire() 187 { 188 if (atomic_add(&fReferences, 1) == 0) 189 acquire_sem(fHasReferences); 190 } 191 192 193 void 194 PrintServerApp::Release() 195 { 196 if (atomic_add(&fReferences, -1) == 1) 197 release_sem(fHasReferences); 198 } 199 200 201 void 202 PrintServerApp::RegisterPrinter(BDirectory* printer) 203 { 204 BString transport, address, connection, state; 205 206 if (printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT, &transport) == B_OK 207 && printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, &address) 208 == B_OK 209 && printer->ReadAttrString(PSRV_PRINTER_ATTR_CNX, &connection) == B_OK 210 && printer->ReadAttrString(PSRV_PRINTER_ATTR_STATE, &state) == B_OK 211 && state == "free") { 212 213 BAutolock lock(gLock); 214 if (lock.IsLocked()) { 215 // check if printer is already registered 216 node_ref node; 217 if (printer->GetNodeRef(&node) != B_OK) 218 return; 219 220 if (Printer::Find(&node) != NULL) 221 return; 222 223 // register new printer 224 Resource* r = fResourceManager.Allocate(transport.String(), 225 address.String(), connection.String()); 226 AddHandler(new Printer(printer, r)); 227 Acquire(); 228 } 229 } 230 } 231 232 233 void 234 PrintServerApp::UnregisterPrinter(Printer* printer) 235 { 236 RemoveHandler(printer); 237 Printer::Remove(printer); 238 printer->Release(); 239 } 240 241 242 void 243 PrintServerApp::NotifyPrinterDeletion(Printer* printer) 244 { 245 BAutolock lock(gLock); 246 if (lock.IsLocked()) { 247 fResourceManager.Free(printer->GetResource()); 248 Release(); 249 } 250 } 251 252 253 void 254 PrintServerApp::EntryCreated(node_ref* node, entry_ref* entry) 255 { 256 BEntry printer(entry); 257 if (printer.InitCheck() == B_OK && printer.IsDirectory()) { 258 BDirectory dir(&printer); 259 if (dir.InitCheck() == B_OK) RegisterPrinter(&dir); 260 } 261 } 262 263 264 void 265 PrintServerApp::EntryRemoved(node_ref* node) 266 { 267 Printer* printer = Printer::Find(node); 268 if (printer) { 269 if (printer == fDefaultPrinter) fDefaultPrinter = NULL; 270 UnregisterPrinter(printer); 271 } 272 } 273 274 275 void 276 PrintServerApp::AttributeChanged(node_ref* node) 277 { 278 BDirectory printer(node); 279 if (printer.InitCheck() == B_OK) { 280 RegisterPrinter(&printer); 281 } 282 } 283 284 285 // --------------------------------------------------------------- 286 // SetupPrinterList 287 // 288 // This method builds the internal list of printers from disk. It 289 // also installs a node monitor to be sure that the list keeps 290 // updated with the definitions on disk. 291 // 292 // Parameters: 293 // none. 294 // 295 // Returns: 296 // B_OK if successful, or an errorcode if failed. 297 // --------------------------------------------------------------- 298 status_t 299 PrintServerApp::SetupPrinterList() 300 { 301 status_t rc; 302 303 // Find directory containing printer definition nodes 304 BPath path; 305 rc = ::find_directory(B_USER_PRINTERS_DIRECTORY, &path); 306 if (rc != B_OK) 307 return rc; 308 309 // Directory has to exist in order to watch it 310 mode_t mode = 0777; 311 create_directory(path.Path(), mode); 312 313 BDirectory dir(path.Path()); 314 rc = dir.InitCheck(); 315 if (rc != B_OK) 316 return rc; 317 318 // Register printer definition nodes 319 BEntry entry; 320 while(dir.GetNextEntry(&entry) == B_OK) { 321 if (!entry.IsDirectory()) 322 continue; 323 324 BDirectory node(&entry); 325 BNodeInfo info(&node); 326 char buffer[256]; 327 if (info.GetType(buffer) == B_OK 328 && strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) { 329 RegisterPrinter(&node); 330 } 331 } 332 333 // Now we are ready to start node watching 334 fFolder = new FolderWatcher(this, dir, true); 335 fFolder->SetListener(this); 336 337 return B_OK; 338 } 339 340 // --------------------------------------------------------------- 341 // void MessageReceived(BMessage* msg) 342 // 343 // Message handling method for print_server application class. 344 // 345 // Parameters: 346 // msg - Actual message sent to application class. 347 // 348 // Returns: 349 // void. 350 // --------------------------------------------------------------- 351 void 352 PrintServerApp::MessageReceived(BMessage* msg) 353 { 354 switch(msg->what) { 355 case PSRV_GET_ACTIVE_PRINTER: 356 case PSRV_MAKE_PRINTER_ACTIVE_QUIETLY: 357 case PSRV_MAKE_PRINTER_ACTIVE: 358 case PSRV_MAKE_PRINTER: 359 case PSRV_SHOW_PAGE_SETUP: 360 case PSRV_SHOW_PRINT_SETUP: 361 case PSRV_PRINT_SPOOLED_JOB: 362 case PSRV_GET_DEFAULT_SETTINGS: 363 Handle_BeOSR5_Message(msg); 364 break; 365 366 case B_GET_PROPERTY: 367 case B_SET_PROPERTY: 368 case B_CREATE_PROPERTY: 369 case B_DELETE_PROPERTY: 370 case B_COUNT_PROPERTIES: 371 case B_EXECUTE_PROPERTY: 372 HandleScriptingCommand(msg); 373 break; 374 375 default: 376 Inherited::MessageReceived(msg); 377 } 378 } 379 380 381 // --------------------------------------------------------------- 382 // CreatePrinter(const char* printerName, const char* driverName, 383 // const char* connection, const char* transportName, 384 // const char* transportPath) 385 // 386 // Creates printer definition/spool directory. It sets the 387 // attributes of the directory to the values passed and calls 388 // the driver's add_printer method to handle any configuration 389 // needed. 390 // 391 // Parameters: 392 // printerName - Name of printer to create. 393 // driverName - Name of driver to use for this printer. 394 // connection - "Local" or "Network". 395 // transportName - Name of transport driver to use. 396 // transportPath - Configuration data for transport driver. 397 // 398 // Returns: 399 // --------------------------------------------------------------- 400 status_t 401 PrintServerApp::CreatePrinter(const char* printerName, const char* driverName, 402 const char* connection, const char* transportName, 403 const char* transportPath) 404 { 405 status_t rc; 406 407 // Find directory containing printer definitions 408 BPath path; 409 rc = ::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL); 410 if (rc != B_OK) 411 return rc; 412 413 // Create our printer definition/spool directory 414 BDirectory printersDir(path.Path()); 415 BDirectory printer; 416 417 rc = printersDir.CreateDirectory(printerName, &printer); 418 if (rc == B_FILE_EXISTS) { 419 printer.SetTo(&printersDir, printerName); 420 421 BString info; 422 char type[B_MIME_TYPE_LENGTH]; 423 BNodeInfo(&printer).GetType(type); 424 if (strcmp(PSRV_PRINTER_FILETYPE, type) == 0) { 425 BPath path; 426 if (Printer::FindPathToDriver(printerName, &path) == B_OK) { 427 if (fDefaultPrinter) { 428 // the printer exists, but is not the default printer 429 if (strcmp(fDefaultPrinter->Name(), printerName) != 0) 430 rc = B_OK; 431 return rc; 432 } 433 // the printer exists, but no default at all 434 return B_OK; 435 } else { 436 info.SetTo(B_TRANSLATE( 437 "A printer with that name already exists, " 438 "but its driver could not be found! Replace?")); 439 } 440 } else { 441 info.SetTo(B_TRANSLATE( 442 "A printer with that name already exists, " 443 "but it's not usable at all! Replace?")); 444 } 445 446 if (info.Length() != 0) { 447 BAlert *alert = new BAlert("Info", info.String(), 448 B_TRANSLATE("Cancel"), B_TRANSLATE("OK")); 449 alert->SetShortcut(0, B_ESCAPE); 450 if (alert->Go() == 0) 451 return rc; 452 } 453 } else if (rc != B_OK) { 454 return rc; 455 } 456 457 // Set its type to a printer 458 BNodeInfo info(&printer); 459 info.SetType(PSRV_PRINTER_FILETYPE); 460 461 // Store the settings in its attributes 462 printer.WriteAttr(PSRV_PRINTER_ATTR_PRT_NAME, B_STRING_TYPE, 0, printerName, 463 ::strlen(printerName) + 1); 464 printer.WriteAttr(PSRV_PRINTER_ATTR_DRV_NAME, B_STRING_TYPE, 0, driverName, 465 ::strlen(driverName) + 1); 466 printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT, B_STRING_TYPE, 0, 467 transportName, ::strlen(transportName) + 1); 468 printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, B_STRING_TYPE, 0, 469 transportPath, ::strlen(transportPath) + 1); 470 printer.WriteAttr(PSRV_PRINTER_ATTR_CNX, B_STRING_TYPE, 0, connection, 471 ::strlen(connection) + 1); 472 473 rc = Printer::ConfigurePrinter(driverName, printerName); 474 if (rc == B_OK) { 475 // Notify printer driver that a new printer definition node 476 // has been created. 477 printer.WriteAttr(PSRV_PRINTER_ATTR_STATE, B_STRING_TYPE, 0, "free", 478 ::strlen("free")+1); 479 } 480 481 if (rc != B_OK) { 482 BEntry entry; 483 if (printer.GetEntry(&entry) == B_OK) 484 entry.Remove(); 485 } 486 487 return rc; 488 } 489 490 491 // --------------------------------------------------------------- 492 // SelectPrinter(const char* printerName) 493 // 494 // Makes a new printer the active printer. This is done simply 495 // by changing our class attribute fDefaultPrinter, and changing 496 // the icon of the BNode for the printer. Ofcourse, we need to 497 // change the icon of the "old" default printer first back to a 498 // "non-active" printer icon first. 499 // 500 // Parameters: 501 // printerName - Name of the new active printer. 502 // 503 // Returns: 504 // B_OK on success, or error code otherwise. 505 // --------------------------------------------------------------- 506 status_t 507 PrintServerApp::SelectPrinter(const char* printerName) 508 { 509 status_t rc; 510 BNode node; 511 512 // Find the node of the "old" default printer 513 if (fDefaultPrinter != NULL 514 && FindPrinterNode(fDefaultPrinter->Name(), node) == B_OK) { 515 // and remove the custom icon 516 BNodeInfo info(&node); 517 info.SetIcon(NULL, B_MINI_ICON); 518 info.SetIcon(NULL, B_LARGE_ICON); 519 } 520 521 // Find the node for the new default printer 522 rc=FindPrinterNode(printerName, node); 523 if (rc == B_OK) { 524 // and add the custom icon 525 BNodeInfo info(&node); 526 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 527 info.SetIcon(fSelectedIcon, fIconSize); 528 #else 529 info.SetIcon(fSelectedIconMini, B_MINI_ICON); 530 info.SetIcon(fSelectedIconLarge, B_LARGE_ICON); 531 #endif 532 } 533 534 fDefaultPrinter = Printer::Find(printerName); 535 StoreDefaultPrinter(); 536 // update our pref file 537 be_roster->Broadcast(new BMessage(B_PRINTER_CHANGED)); 538 539 return rc; 540 } 541 542 543 // --------------------------------------------------------------- 544 // HandleSpooledJobs() 545 // 546 // Handles calling the printer drivers for printing a spooled job. 547 // 548 // --------------------------------------------------------------- 549 void 550 PrintServerApp::HandleSpooledJobs() 551 { 552 const int n = Printer::CountPrinters(); 553 for (int i = 0; i < n; i ++) { 554 Printer* printer = Printer::At(i); 555 printer->HandleSpooledJob(); 556 } 557 } 558 559 560 // --------------------------------------------------------------- 561 // RetrieveDefaultPrinter() 562 // 563 // Loads the currently selected printer from a private settings 564 // file. 565 // 566 // Parameters: 567 // none. 568 // 569 // Returns: 570 // Error code on failore, or B_OK if all went fine. 571 // --------------------------------------------------------------- 572 status_t 573 PrintServerApp::RetrieveDefaultPrinter() 574 { 575 fDefaultPrinter = Printer::Find(fSettings->DefaultPrinter()); 576 return B_OK; 577 } 578 579 580 // --------------------------------------------------------------- 581 // StoreDefaultPrinter() 582 // 583 // Stores the currently selected printer in a private settings 584 // file. 585 // 586 // Parameters: 587 // none. 588 // 589 // Returns: 590 // Error code on failore, or B_OK if all went fine. 591 // --------------------------------------------------------------- 592 status_t 593 PrintServerApp::StoreDefaultPrinter() 594 { 595 if (fDefaultPrinter) 596 fSettings->SetDefaultPrinter(fDefaultPrinter->Name()); 597 else 598 fSettings->SetDefaultPrinter(""); 599 return B_OK; 600 } 601 602 603 // --------------------------------------------------------------- 604 // FindPrinterNode(const char* name, BNode& node) 605 // 606 // Find the BNode representing the specified printer. It searches 607 // *only* in the users printer definitions. 608 // 609 // Parameters: 610 // name - Name of the printer to look for. 611 // node - BNode to set to the printer definition node. 612 // 613 // Returns: 614 // B_OK if found, an error code otherwise. 615 // --------------------------------------------------------------- 616 status_t 617 PrintServerApp::FindPrinterNode(const char* name, BNode& node) 618 { 619 // Find directory containing printer definitions 620 BPath path; 621 status_t rc = ::find_directory(B_USER_PRINTERS_DIRECTORY, &path, true, NULL); 622 if (rc != B_OK) 623 return rc; 624 625 path.Append(name); 626 return node.SetTo(path.Path()); 627 } 628 629 630 bool 631 PrintServerApp::OpenSettings(BFile& file, const char* name, 632 bool forReading) 633 { 634 BPath path; 635 uint32 openMode = forReading ? B_READ_ONLY : B_CREATE_FILE | B_ERASE_FILE 636 | B_WRITE_ONLY; 637 return find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK 638 && path.Append(name) == B_OK 639 && file.SetTo(path.Path(), openMode) == B_OK; 640 } 641 642 643 void 644 PrintServerApp::LoadSettings() { 645 BFile file; 646 if (OpenSettings(file, kSettingsName, true)) { 647 fSettings->Load(&file); 648 fUseConfigWindow = fSettings->UseConfigWindow(); 649 } 650 } 651 652 653 void 654 PrintServerApp::SaveSettings() { 655 BFile file; 656 if (OpenSettings(file, kSettingsName, false)) { 657 fSettings->SetUseConfigWindow(fUseConfigWindow); 658 fSettings->Save(&file); 659 } 660 } 661