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