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 "Printer.h" 39 40 #include "pr_server.h" 41 #include "BeUtils.h" 42 #include "PrintServerApp.h" 43 44 // posix 45 #include <limits.h> 46 #include <stdlib.h> 47 #include <string.h> 48 49 // BeOS API 50 #include <Application.h> 51 #include <Autolock.h> 52 #include <Message.h> 53 #include <NodeMonitor.h> 54 #include <String.h> 55 #include <StorageKit.h> 56 #include <SupportDefs.h> 57 58 59 SpoolFolder::SpoolFolder(BLocker*locker, BLooper* looper, const BDirectory& spoolDir) 60 : Folder(locker, looper, spoolDir) 61 { 62 } 63 64 65 // Notify print_server that there is a job file waiting for printing 66 void SpoolFolder::Notify(Job* job, int kind) 67 { 68 if ((kind == kJobAdded || kind == kJobAttrChanged) && 69 job->IsValid() && job->IsWaiting()) { 70 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB); 71 } 72 } 73 74 75 // --------------------------------------------------------------- 76 typedef BMessage* (*config_func_t)(BNode*, const BMessage*); 77 typedef BMessage* (*take_job_func_t)(BFile*, BNode*, const BMessage*); 78 typedef char* (*add_printer_func_t)(const char* printer_name); 79 typedef BMessage* (*default_settings_t)(BNode*); 80 81 // --------------------------------------------------------------- 82 BObjectList<Printer> Printer::sPrinters; 83 84 85 // --------------------------------------------------------------- 86 // Find [static] 87 // 88 // Searches the static object list for a printer object with the 89 // specified name. 90 // 91 // Parameters: 92 // name - Printer definition name we're looking for. 93 // 94 // Returns: 95 // Pointer to Printer object, or NULL if not found. 96 // --------------------------------------------------------------- 97 Printer* Printer::Find(const BString& name) 98 { 99 // Look in list to find printer definition 100 for (int32 idx=0; idx < sPrinters.CountItems(); idx++) { 101 if (name == sPrinters.ItemAt(idx)->Name()) { 102 return sPrinters.ItemAt(idx); 103 } 104 } 105 106 // None found, so return NULL 107 return NULL; 108 } 109 110 Printer* Printer::Find(node_ref* node) 111 { 112 node_ref n; 113 // Look in list to find printer definition 114 for (int32 idx=0; idx < sPrinters.CountItems(); idx++) { 115 Printer* printer = sPrinters.ItemAt(idx); 116 printer->SpoolDir()->GetNodeRef(&n); 117 if (n == *node) return printer; 118 } 119 120 // None found, so return NULL 121 return NULL; 122 } 123 124 Printer* Printer::At(int32 idx) 125 { 126 return sPrinters.ItemAt(idx); 127 } 128 129 void Printer::Remove(Printer* printer) { 130 sPrinters.RemoveItem(printer); 131 } 132 133 int32 Printer::CountPrinters() 134 { 135 return sPrinters.CountItems(); 136 } 137 138 // --------------------------------------------------------------- 139 // Printer [constructor] 140 // 141 // Initializes the printer object with data read from the 142 // attributes attached to the printer definition node. 143 // 144 // Parameters: 145 // node - Printer definition node for this printer. 146 // 147 // Returns: 148 // none. 149 // --------------------------------------------------------------- 150 Printer::Printer(const BDirectory* node, Resource* res) 151 : Inherited(B_EMPTY_STRING), 152 fPrinter(gLock, be_app, *node), 153 fResource(res), 154 fSinglePrintThread(true), 155 fJob(NULL), 156 fProcessing(0), 157 fAbort(false) 158 { 159 BString name; 160 // Set our name to the name of the passed node 161 if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) == B_OK) 162 SetName(name.String()); 163 164 if (name == "Preview") fSinglePrintThread = false; 165 166 // Add us to the global list of known printer definitions 167 sPrinters.AddItem(this); 168 } 169 170 Printer::~Printer() 171 { 172 ((PrintServerApp*)be_app)->NotifyPrinterDeletion(this); 173 } 174 175 176 // Remove printer spooler directory 177 status_t Printer::Remove() 178 { 179 status_t rc = B_OK; 180 BPath path; 181 182 if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) { 183 path.Append(Name()); 184 rc = rmdir(path.Path()); 185 } 186 187 return rc; 188 } 189 190 // --------------------------------------------------------------- 191 // ConfigurePrinter 192 // 193 // Handles calling the printer addon's add_printer function. 194 // 195 // Parameters: 196 // none. 197 // 198 // Returns: 199 // B_OK if successful or errorcode otherwise. 200 // --------------------------------------------------------------- 201 status_t Printer::ConfigurePrinter() 202 { 203 image_id id; 204 status_t rc; 205 206 if ((rc=LoadPrinterAddon(id)) == B_OK) { 207 // Addon was loaded, so try and get the add_printer symbol 208 add_printer_func_t func; 209 210 if (get_image_symbol(id, "add_printer", B_SYMBOL_TYPE_TEXT, (void**)&func) == B_OK) { 211 // call the function and check its result 212 rc = ((*func)(Name()) == NULL) ? B_ERROR : B_OK; 213 } 214 215 ::unload_add_on(id); 216 } 217 218 return rc; 219 } 220 221 // --------------------------------------------------------------- 222 // ConfigurePage 223 // 224 // Handles calling the printer addon's config_page function. 225 // 226 // Parameters: 227 // settings - Page settings to display. The contents of this 228 // message will be replaced with the new settings 229 // if the function returns success. 230 // 231 // Returns: 232 // B_OK if successful or errorcode otherwise. 233 // --------------------------------------------------------------- 234 status_t Printer::ConfigurePage(BMessage& settings) 235 { 236 image_id id; 237 status_t rc; 238 239 if ((rc=LoadPrinterAddon(id)) == B_OK) { 240 // Addon was loaded, so try and get the config_page symbol 241 config_func_t func; 242 243 if ((rc=get_image_symbol(id, "config_page", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 244 // call the function and check its result 245 BMessage* new_settings = (*func)(SpoolDir(), &settings); 246 if (new_settings != NULL && new_settings->what != 'baad') { 247 settings = *new_settings; 248 AddCurrentPrinter(&settings); 249 } else 250 rc = B_ERROR; 251 delete new_settings; 252 } 253 254 ::unload_add_on(id); 255 } 256 257 return rc; 258 } 259 260 // --------------------------------------------------------------- 261 // ConfigureJob 262 // 263 // Handles calling the printer addon's config_job function. 264 // 265 // Parameters: 266 // settings - Job settings to display. The contents of this 267 // message will be replaced with the new settings 268 // if the function returns success. 269 // 270 // Returns: 271 // B_OK if successful or errorcode otherwise. 272 // --------------------------------------------------------------- 273 status_t Printer::ConfigureJob(BMessage& settings) 274 { 275 image_id id; 276 status_t rc; 277 278 if ((rc=LoadPrinterAddon(id)) == B_OK) { 279 // Addon was loaded, so try and get the config_job symbol 280 config_func_t func; 281 282 if ((rc=get_image_symbol(id, "config_job", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 283 // call the function and check its result 284 BMessage* new_settings = (*func)(SpoolDir(), &settings); 285 if ((new_settings != NULL) && (new_settings->what != 'baad')) { 286 settings = *new_settings; 287 AddCurrentPrinter(&settings); 288 } else 289 rc = B_ERROR; 290 delete new_settings; 291 } 292 293 ::unload_add_on(id); 294 } 295 296 return rc; 297 } 298 299 300 // --------------------------------------------------------------- 301 // HandleSpooledJobs 302 // 303 // Print spooled jobs in a new thread. 304 // --------------------------------------------------------------- 305 void Printer::HandleSpooledJob() { 306 BAutolock lock(gLock); 307 if (lock.IsLocked() && (!fSinglePrintThread || fProcessing == 0) && FindSpooledJob()) { 308 StartPrintThread(); 309 } 310 } 311 312 313 // --------------------------------------------------------------- 314 // GetDefaultSettings 315 // 316 // Retrieve the default configuration message from printer add-on 317 // 318 // Parameters: 319 // settings, output paramter. 320 // 321 // Returns: 322 // B_OK if successful or errorcode otherwise. 323 // --------------------------------------------------------------- 324 status_t Printer::GetDefaultSettings(BMessage& settings) { 325 image_id id; 326 status_t rc; 327 328 if ((rc=LoadPrinterAddon(id)) == B_OK) { 329 // Addon was loaded, so try and get the default_settings symbol 330 default_settings_t func; 331 332 if ((rc=get_image_symbol(id, "default_settings", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 333 // call the function and check its result 334 BMessage* new_settings = (*func)(SpoolDir()); 335 if (new_settings) { 336 settings = *new_settings; 337 AddCurrentPrinter(&settings); 338 } else { 339 rc = B_ERROR; 340 } 341 delete new_settings; 342 } 343 344 ::unload_add_on(id); 345 } 346 347 return rc; 348 } 349 350 351 void Printer::AbortPrintThread() { 352 fAbort = true; 353 } 354 355 // --------------------------------------------------------------- 356 // LoadPrinterAddon 357 // 358 // Try to load the printer addon into memory. 359 // 360 // Parameters: 361 // id - image_id set to the image id of the loaded addon. 362 // 363 // Returns: 364 // B_OK if successful or errorcode otherwise. 365 // --------------------------------------------------------------- 366 status_t Printer::LoadPrinterAddon(image_id& id) 367 { 368 BString drName; 369 status_t rc; 370 BPath path; 371 372 if ((rc=SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_DRV_NAME, &drName)) == B_OK) { 373 // try to locate the driver 374 if ((rc=::TestForAddonExistence(drName.String(), B_USER_ADDONS_DIRECTORY, "Print", path)) != B_OK) { 375 if ((rc=::TestForAddonExistence(drName.String(), B_COMMON_ADDONS_DIRECTORY, "Print", path)) != B_OK) { 376 rc = ::TestForAddonExistence(drName.String(), B_BEOS_ADDONS_DIRECTORY, "Print", path); 377 } 378 } 379 380 // If the driver was found 381 if (rc == B_OK) { 382 // If we cannot load the addon 383 if ((id=::load_add_on(path.Path())) < 0) 384 rc = id; 385 } 386 } 387 return rc; 388 } 389 390 void Printer::AddCurrentPrinter(BMessage* msg) 391 { 392 BString name; 393 GetName(name); 394 if (msg->HasString(PSRV_FIELD_CURRENT_PRINTER)) { 395 msg->ReplaceString(PSRV_FIELD_CURRENT_PRINTER, name.String()); 396 } else { 397 msg->AddString(PSRV_FIELD_CURRENT_PRINTER, name.String()); 398 } 399 } 400 401 void Printer::MessageReceived(BMessage* msg) 402 { 403 switch(msg->what) { 404 case B_GET_PROPERTY: 405 case B_SET_PROPERTY: 406 case B_CREATE_PROPERTY: 407 case B_DELETE_PROPERTY: 408 case B_COUNT_PROPERTIES: 409 case B_EXECUTE_PROPERTY: 410 HandleScriptingCommand(msg); 411 break; 412 413 default: 414 Inherited::MessageReceived(msg); 415 } 416 } 417 418 void Printer::GetName(BString& name) { 419 if (B_OK != SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name)) 420 name = "Unknown Printer"; 421 } 422 423 bool Printer::HasCurrentPrinter(BString& name) { 424 BMessage settings; 425 // read settings from spool file and get printer name 426 BFile jobFile(&fJob->EntryRef(), B_READ_WRITE); 427 return jobFile.InitCheck() == B_OK && 428 jobFile.Seek(sizeof(print_file_header), SEEK_SET) == sizeof(print_file_header) && 429 settings.Unflatten(&jobFile) == B_OK && 430 settings.FindString(PSRV_FIELD_CURRENT_PRINTER, &name) == B_OK; 431 } 432 433 bool Printer::MoveJob(const BString& name) { 434 BPath file(&fJob->EntryRef()); 435 BPath path; 436 file.GetParent(&path); 437 path.Append(".."); path.Append(name.String()); 438 BDirectory dir(path.Path()); 439 BEntry entry(&fJob->EntryRef()); 440 // try to move job file to proper directory 441 return entry.MoveTo(&dir) == B_OK; 442 } 443 444 bool Printer::FindSpooledJob() { 445 BString name2; 446 GetName(name2); 447 do { 448 fJob = fPrinter.GetNextJob(); 449 if (fJob) { 450 BString name; 451 if (HasCurrentPrinter(name) && name != name2 && MoveJob(name)) { 452 fJob->SetStatus(kUnknown, false); // so that fPrinter.GetNextJob skips it 453 fJob->Release(); 454 } else { 455 fJob->SetPrinter(this); 456 return true; 457 } 458 } 459 } while (fJob != NULL); 460 return false; 461 } 462 463 status_t Printer::PrintSpooledJob(BFile* spoolFile) 464 { 465 take_job_func_t func; 466 image_id id; 467 status_t rc; 468 469 if ((rc=LoadPrinterAddon(id)) == B_OK) { 470 // Addon was loaded, so try and get the take_job symbol 471 if ((rc=get_image_symbol(id, "take_job", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 472 // This seems to be required for legacy? 473 // HP PCL3 add-on crashes without it! 474 BMessage params('_RRC'); 475 params.AddInt32("file", (int32)spoolFile); 476 params.AddInt32("printer", (int32)SpoolDir()); 477 // call the function and check its result 478 BMessage* result = (*func)(spoolFile, SpoolDir(), ¶ms); 479 480 if (result == NULL || result->what != 'okok') 481 rc = B_ERROR; 482 483 delete result; 484 } 485 486 ::unload_add_on(id); 487 } 488 489 return rc; 490 } 491 492 493 void Printer::PrintThread(Job* job) { 494 // Wait until resource is available 495 fResource->Lock(); 496 bool failed = true; 497 // Can we continue? 498 if (!fAbort) { 499 BFile jobFile(&job->EntryRef(), B_READ_WRITE); 500 // Tell the printer to print the spooled job 501 if (jobFile.InitCheck() == B_OK && PrintSpooledJob(&jobFile) == B_OK) { 502 // Remove spool file if printing was successfull. 503 job->Remove(); failed = false; 504 } 505 } 506 // Set status of spooled job on error 507 if (failed) job->SetStatus(kFailed); 508 fResource->Unlock(); 509 job->Release(); 510 atomic_add(&fProcessing, -1); 511 Release(); 512 // Notify print_server to process next spooled job 513 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB); 514 } 515 516 status_t Printer::print_thread(void* data) { 517 Job* job = static_cast<Job*>(data); 518 job->GetPrinter()->PrintThread(job); 519 return 0; 520 } 521 522 void Printer::StartPrintThread() { 523 Acquire(); 524 thread_id tid = spawn_thread(print_thread, "print", B_NORMAL_PRIORITY, (void*)fJob); 525 if (tid > 0) { 526 fJob->SetStatus(kProcessing); 527 atomic_add(&fProcessing, 1); 528 resume_thread(tid); 529 } else { 530 fJob->Release(); Release(); 531 } 532 } 533 534