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