1 /*****************************************************************************/ 2 // print_server Background Application. 3 // 4 // Version: 1.0.0d1 5 // 6 // The print_server manages the communication between applications and the 7 // printer and transport drivers. 8 // 9 // Authors 10 // Ithamar R. Adema 11 // Michael Pfeiffer 12 // 13 // This application and all source files used in its construction, except 14 // where noted, are licensed under the MIT License, and have been written 15 // and are: 16 // 17 // Copyright (c) 2001, 2002 OpenBeOS Project 18 // 19 // Permission is hereby granted, free of charge, to any person obtaining a 20 // copy of this software and associated documentation files (the "Software"), 21 // to deal in the Software without restriction, including without limitation 22 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 // and/or sell copies of the Software, and to permit persons to whom the 24 // Software is furnished to do so, subject to the following conditions: 25 // 26 // The above copyright notice and this permission notice shall be included 27 // in all copies or substantial portions of the Software. 28 // 29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 30 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 // DEALINGS IN THE SOFTWARE. 36 /*****************************************************************************/ 37 38 #include "PrintServerApp.h" 39 40 #include "BeUtils.h" 41 #include "Printer.h" 42 #include "pr_server.h" 43 44 // BeOS API 45 #include <Alert.h> 46 #include <Autolock.h> 47 #include <Directory.h> 48 #include <File.h> 49 #include <image.h> 50 #include <FindDirectory.h> 51 #include <Mime.h> 52 #include <NodeInfo.h> 53 #include <NodeMonitor.h> 54 #include <Path.h> 55 #include <Roster.h> 56 #include <PrintJob.h> 57 #include <String.h> 58 59 // ANSI C 60 #include <stdio.h> //for printf 61 #include <unistd.h> //for unlink 62 63 BLocker *gLock = NULL; 64 65 // --------------------------------------------------------------- 66 typedef struct _printer_data { 67 char defaultPrinterName[256]; 68 } printer_data_t; 69 70 typedef BMessage* (*config_func_t)(BNode*, const BMessage*); 71 typedef BMessage* (*take_job_func_t)(BFile*, BNode*, const BMessage*); 72 typedef char* (*add_printer_func_t)(const char* printer_name); 73 74 /** 75 * Main entry point of print_server. 76 * 77 * @returns B_OK if application was started, or an errorcode if 78 * application failed to start. 79 * 80 * @see your favorite C/C++ textbook :P 81 */ 82 83 int 84 main() 85 { 86 status_t rc = B_OK; 87 gLock = new BLocker(); 88 // Create our application object 89 PrintServerApp print_server(&rc); 90 // If all went fine, let's start it 91 if (rc == B_OK) { 92 print_server.Run(); 93 } 94 delete gLock; 95 return rc; 96 } 97 98 /** 99 * Constructor for print_server's application class. Retrieves the 100 * name of the default printer from storage, caches the icons for 101 * a selected printer. 102 * 103 * @param err Pointer to status_t for storing result of application 104 * initialisation. 105 * 106 * @see BApplication 107 */ 108 109 PrintServerApp::PrintServerApp(status_t* err) 110 : Inherited(PSRV_SIGNATURE_TYPE, err), 111 fSelectedIconMini(NULL), 112 fSelectedIconLarge(NULL), 113 fReferences(0), 114 fHasReferences(0), 115 fUseConfigWindow(true), 116 fFolder(NULL) 117 { 118 fSettings = Settings::GetSettings(); 119 LoadSettings(); 120 // If our superclass initialized ok 121 if (*err == B_OK) { 122 fHasReferences = create_sem(1, "has_references"); 123 124 // let us try as well 125 SetupPrinterList(); 126 RetrieveDefaultPrinter(); 127 128 fSelectedIconMini = new BBitmap(BRect(0,0,B_MINI_ICON-1,B_MINI_ICON-1), B_CMAP8); 129 fSelectedIconLarge = new BBitmap(BRect(0,0,B_LARGE_ICON-1,B_LARGE_ICON-1), B_CMAP8); 130 131 // Cache icons for selected printer 132 BMimeType type(PRNT_SIGNATURE_TYPE); 133 type.GetIcon(fSelectedIconMini, B_MINI_ICON); 134 type.GetIcon(fSelectedIconLarge, B_LARGE_ICON); 135 136 // Start handling of spooled files 137 PostMessage(PSRV_PRINT_SPOOLED_JOB); 138 } 139 } 140 141 PrintServerApp::~PrintServerApp() { 142 SaveSettings(); 143 delete fSettings; 144 } 145 146 bool PrintServerApp::QuitRequested() 147 { 148 BMessage* m = CurrentMessage(); 149 bool shortcut; 150 // don't quit when user types Command+Q! 151 if (m && m->FindBool("shortcut", &shortcut) == B_OK && shortcut) return false; 152 153 bool rc = Inherited::QuitRequested(); 154 if (rc) { 155 delete fFolder; fFolder = NULL; 156 #if 0 157 // Find directory containing printer definition nodes 158 BPath path; 159 if (::find_directory(B_USER_PRINTERS_DIRECTORY, &path) == B_OK) { 160 BEntry entry(path.Path()); 161 node_ref nref; 162 if (entry.InitCheck() == B_OK && 163 entry.GetNodeRef(&nref) == B_OK) { 164 // Stop watching the printer directory 165 ::watch_node(&nref, B_STOP_WATCHING, be_app_messenger); 166 } 167 } 168 #endif 169 170 // Release all printers 171 Printer* printer; 172 while ((printer = Printer::At(0)) != NULL) { 173 printer->AbortPrintThread(); 174 UnregisterPrinter(printer); 175 } 176 177 // Wait for printers 178 if (fHasReferences > 0) { 179 acquire_sem(fHasReferences); 180 delete_sem(fHasReferences); 181 fHasReferences = 0; 182 } 183 184 ASSERT(fSelectedIconMini != NULL); 185 186 delete fSelectedIconMini; fSelectedIconMini = NULL; 187 delete fSelectedIconLarge; fSelectedIconLarge = NULL; 188 } 189 190 return rc; 191 } 192 193 void PrintServerApp::Acquire() { 194 if (atomic_add(&fReferences, 1) == 0) acquire_sem(fHasReferences); 195 } 196 197 void PrintServerApp::Release() { 198 if (atomic_add(&fReferences, -1) == 1) release_sem(fHasReferences); 199 } 200 201 void PrintServerApp::RegisterPrinter(BDirectory* printer) { 202 BString transport, address, connection, state; 203 204 if (printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT, &transport) == B_OK && 205 printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, &address) == B_OK && 206 printer->ReadAttrString(PSRV_PRINTER_ATTR_CNX, &connection) == B_OK && 207 printer->ReadAttrString(PSRV_PRINTER_ATTR_STATE, &state) == B_OK && 208 state == "free") { 209 210 BAutolock lock(gLock); 211 if (lock.IsLocked()) { 212 // check if printer is already registered 213 node_ref node; 214 Printer* p; 215 if (printer->GetNodeRef(&node) != B_OK) return; 216 p = Printer::Find(&node); 217 if (p != NULL) return; 218 // register new printer 219 Resource* r = fResourceManager.Allocate(transport.String(), address.String(), connection.String()); 220 p = new Printer(printer, r); 221 AddHandler(p); 222 Acquire(); 223 } 224 } 225 } 226 227 void PrintServerApp::UnregisterPrinter(Printer* printer) { 228 RemoveHandler(printer); 229 Printer::Remove(printer); 230 printer->Release(); 231 } 232 233 void PrintServerApp::NotifyPrinterDeletion(Printer* printer) { 234 BAutolock lock(gLock); 235 if (lock.IsLocked()) { 236 fResourceManager.Free(printer->GetResource()); 237 Release(); 238 } 239 } 240 241 242 void PrintServerApp::EntryCreated(node_ref* node, entry_ref* entry) { 243 BEntry printer(entry); 244 if (printer.InitCheck() == B_OK && printer.IsDirectory()) { 245 BDirectory dir(&printer); 246 if (dir.InitCheck() == B_OK) RegisterPrinter(&dir); 247 } 248 } 249 250 void PrintServerApp::EntryRemoved(node_ref* node) { 251 Printer* printer = Printer::Find(node); 252 if (printer) { 253 if (printer == fDefaultPrinter) fDefaultPrinter = NULL; 254 UnregisterPrinter(printer); 255 } 256 } 257 258 void PrintServerApp::AttributeChanged(node_ref* node) { 259 BDirectory printer(node); 260 if (printer.InitCheck() == B_OK) { 261 RegisterPrinter(&printer); 262 } 263 } 264 265 // --------------------------------------------------------------- 266 // SetupPrinterList 267 // 268 // This method builds the internal list of printers from disk. It 269 // also installs a node monitor to be sure that the list keeps 270 // updated with the definitions on disk. 271 // 272 // Parameters: 273 // none. 274 // 275 // Returns: 276 // B_OK if successful, or an errorcode if failed. 277 // --------------------------------------------------------------- 278 status_t PrintServerApp::SetupPrinterList() 279 { 280 BEntry entry; 281 status_t rc; 282 BPath path; 283 284 // Find directory containing printer definition nodes 285 if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) { 286 BDirectory dir(path.Path()); 287 288 // Can we reach the directory? 289 if ((rc=dir.InitCheck()) == B_OK) { 290 // Yes, so loop over it's entries 291 while((rc=dir.GetNextEntry(&entry)) == B_OK) { 292 // If the entry is a directory 293 if (entry.IsDirectory()) { 294 // Check it's Mime type for a spool director 295 BDirectory node(&entry); 296 BNodeInfo info(&node); 297 char buffer[256]; 298 299 if (info.GetType(buffer) == B_OK && 300 strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) { 301 // Yes, it is a printer definition node 302 RegisterPrinter(&node); 303 } 304 } 305 } 306 307 308 fFolder = new FolderWatcher(this, dir, true); 309 fFolder->SetListener(this); 310 #if 0 311 // If we scanned all entries successfully 312 node_ref nref; 313 if (rc == B_ENTRY_NOT_FOUND && 314 (rc=dir.GetNodeRef(&nref)) == B_OK) { 315 // Get node to watch 316 rc = ::watch_node(&nref, B_WATCH_DIRECTORY, be_app_messenger); 317 } 318 #endif 319 } 320 } 321 322 return rc; 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 if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL)) == B_OK) { 393 394 // Create our printer definition/spool directory 395 BDirectory printersDir(path.Path()); 396 BDirectory printer; 397 if ((rc=printersDir.CreateDirectory(printerName,&printer)) == B_OK) { 398 // Set its type to a printer 399 BNodeInfo info(&printer); 400 info.SetType(PSRV_PRINTER_FILETYPE); 401 402 // Store the settings in its attributes 403 printer.WriteAttr(PSRV_PRINTER_ATTR_PRT_NAME, B_STRING_TYPE, 0, printerName, ::strlen(printerName)+1); 404 printer.WriteAttr(PSRV_PRINTER_ATTR_DRV_NAME, B_STRING_TYPE, 0, driverName, ::strlen(driverName)+1); 405 printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT, B_STRING_TYPE, 0, transportName,::strlen(transportName)+1); 406 printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, B_STRING_TYPE, 0, transportPath,::strlen(transportPath)+1); 407 printer.WriteAttr(PSRV_PRINTER_ATTR_CNX, B_STRING_TYPE, 0, connection, ::strlen(connection)+1); 408 409 // Find printer driver 410 if (FindPrinterDriver(driverName, path) == B_OK) { 411 // Try and load the addon 412 image_id id = ::load_add_on(path.Path()); 413 if (id > 0) { 414 // Addon was loaded, so try and get the add_printer symbol 415 add_printer_func_t func; 416 if (get_image_symbol(id, "add_printer", B_SYMBOL_TYPE_TEXT, (void**)&func) == B_OK) { 417 // call the function and check its result 418 if ((*func)(printerName) == NULL) { 419 BEntry entry; 420 if (printer.GetEntry(&entry) == B_OK) { 421 // Delete the printer if function failed 422 entry.Remove(); 423 } 424 } else { 425 printer.WriteAttr(PSRV_PRINTER_ATTR_STATE, B_STRING_TYPE, 0, "free", ::strlen("free")+1); 426 } 427 } 428 429 ::unload_add_on(id); 430 } 431 } 432 } 433 } 434 435 return rc; 436 } 437 438 // --------------------------------------------------------------- 439 // SelectPrinter(const char* printerName) 440 // 441 // Makes a new printer the active printer. This is done simply 442 // by changing our class attribute fDefaultPrinter, and changing 443 // the icon of the BNode for the printer. Ofcourse, we need to 444 // change the icon of the "old" default printer first back to a 445 // "non-active" printer icon first. 446 // 447 // Parameters: 448 // printerName - Name of the new active printer. 449 // 450 // Returns: 451 // B_OK on success, or error code otherwise. 452 // --------------------------------------------------------------- 453 status_t 454 PrintServerApp::SelectPrinter(const char* printerName) 455 { 456 status_t rc; 457 BNode node; 458 459 // Find the node of the "old" default printer 460 if (fDefaultPrinter != NULL && 461 FindPrinterNode(fDefaultPrinter->Name(), node) == B_OK) { 462 // and remove the custom icon 463 BNodeInfo info(&node); 464 info.SetIcon(NULL, B_MINI_ICON); 465 info.SetIcon(NULL, B_LARGE_ICON); 466 } 467 468 // Find the node for the new default printer 469 if ((rc=FindPrinterNode(printerName, node)) == B_OK) { 470 // and add the custom icon 471 BNodeInfo info(&node); 472 info.SetIcon(fSelectedIconMini, B_MINI_ICON); 473 info.SetIcon(fSelectedIconLarge, B_LARGE_ICON); 474 } 475 476 fDefaultPrinter = Printer::Find(printerName); 477 StoreDefaultPrinter(); // update our pref file 478 be_roster->Broadcast(new BMessage(B_PRINTER_CHANGED)); 479 480 return rc; 481 } 482 483 // --------------------------------------------------------------- 484 // HandleSpooledJobs() 485 // 486 // Handles calling the printer drivers for printing a spooled job. 487 // 488 // --------------------------------------------------------------- 489 void 490 PrintServerApp::HandleSpooledJobs() 491 { 492 const int n = Printer::CountPrinters(); 493 for (int i = 0; i < n; i ++) { 494 Printer* printer = Printer::At(i); 495 printer->HandleSpooledJob(); 496 } 497 } 498 499 // static const char* kPrinterData = "printer_data"; 500 501 // --------------------------------------------------------------- 502 // RetrieveDefaultPrinter() 503 // 504 // Loads the currently selected printer from a private settings 505 // file. 506 // 507 // Parameters: 508 // none. 509 // 510 // Returns: 511 // Error code on failore, or B_OK if all went fine. 512 // --------------------------------------------------------------- 513 status_t 514 PrintServerApp::RetrieveDefaultPrinter() 515 { 516 fDefaultPrinter = Printer::Find(fSettings->DefaultPrinter()); 517 return B_OK; 518 } 519 520 // --------------------------------------------------------------- 521 // StoreDefaultPrinter() 522 // 523 // Stores the currently selected printer in a private settings 524 // file. 525 // 526 // Parameters: 527 // none. 528 // 529 // Returns: 530 // Error code on failore, or B_OK if all went fine. 531 // --------------------------------------------------------------- 532 status_t 533 PrintServerApp::StoreDefaultPrinter() 534 { 535 if (fDefaultPrinter) 536 fSettings->SetDefaultPrinter(fDefaultPrinter->Name()); 537 else 538 fSettings->SetDefaultPrinter(""); 539 return B_OK; 540 } 541 542 // --------------------------------------------------------------- 543 // FindPrinterNode(const char* name, BNode& node) 544 // 545 // Find the BNode representing the specified printer. It searches 546 // *only* in the users printer definitions. 547 // 548 // Parameters: 549 // name - Name of the printer to look for. 550 // node - BNode to set to the printer definition node. 551 // 552 // Returns: 553 // B_OK if found, an error code otherwise. 554 // --------------------------------------------------------------- 555 status_t 556 PrintServerApp::FindPrinterNode(const char* name, BNode& node) 557 { 558 status_t rc; 559 560 // Find directory containing printer definitions 561 BPath path; 562 if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL)) == B_OK) 563 { 564 path.Append(name); 565 rc = node.SetTo(path.Path()); 566 } 567 568 return rc; 569 } 570 571 // --------------------------------------------------------------- 572 // FindPrinterDriver(const char* name, BPath& outPath) 573 // 574 // Finds the path to a specific printer driver. It searches all 3 575 // places add-ons can be stored: the user's private directory, the 576 // directory common to all users, and the system directory, in that 577 // order. 578 // 579 // Parameters: 580 // name - Name of the printer driver to look for. 581 // outPath - BPath to store the path to the driver in. 582 // 583 // Returns: 584 // B_OK if the driver was found, otherwise an error code. 585 // --------------------------------------------------------------- 586 status_t 587 PrintServerApp::FindPrinterDriver(const char* name, BPath& outPath) 588 { 589 // try to locate the driver 590 if (::TestForAddonExistence(name, B_USER_ADDONS_DIRECTORY, "Print", outPath) != B_OK) 591 if (::TestForAddonExistence(name, B_COMMON_ADDONS_DIRECTORY, "Print", outPath) != B_OK) 592 return ::TestForAddonExistence(name, B_BEOS_ADDONS_DIRECTORY, "Print", outPath); 593 594 return B_OK; 595 } 596 597 598 bool PrintServerApp::OpenSettings(BFile& file, const char* name, bool forReading) { 599 BPath path; 600 uint32 openMode = forReading ? B_READ_ONLY : B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY; 601 return find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK && 602 path.Append(name) == B_OK && 603 file.SetTo(path.Path(), openMode) == B_OK; 604 } 605 606 static const char* kSettingsName = "print_server_settings"; 607 608 void PrintServerApp::LoadSettings() { 609 BFile file; 610 if (OpenSettings(file, kSettingsName, true)) { 611 fSettings->Load(&file); 612 fUseConfigWindow = fSettings->UseConfigWindow(); 613 } 614 } 615 616 void PrintServerApp::SaveSettings() { 617 BFile file; 618 if (OpenSettings(file, kSettingsName, false)) { 619 fSettings->SetUseConfigWindow(fUseConfigWindow); 620 fSettings->Save(&file); 621 } 622 } 623