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