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