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