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