• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 21108840797

18 Jan 2026 08:35AM UTC coverage: 60.508% (+0.02%) from 60.492%
21108840797

push

github

web-flow
ClangTidy readability-else-after-return (#857)

* trigger tidy

* Automated clang-tidy change: readability-else-after-return

* compile test

* rerun

* Automated clang-tidy change: readability-else-after-return

* trigger..

* Automated clang-tidy change: readability-else-after-return

* manually removed a few

* Automated clang-tidy change: readability-else-after-return

* removed duplicate code

* fix remaining warnings

* fixed scope

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

837 of 1874 new or added lines in 151 files covered. (44.66%)

46 existing lines in 25 files now uncovered.

44448 of 73458 relevant lines covered (60.51%)

525066.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.5
/pol-core/pol/module/osmod.cpp
1
/** @file
2
 *
3
 * @par History
4
 */
5

6
#include "osmod.h"
7

8
#include "bscript/berror.h"
9
#include "bscript/bobject.h"
10
#include "bscript/bstruct.h"
11
#include "bscript/dict.h"
12
#include "bscript/impstr.h"
13
#include "clib/clib.h"
14
#include "clib/logfacility.h"
15
#include "clib/network/sckutil.h"
16
#include "clib/rawtypes.h"
17
#include "clib/refptr.h"
18
#include "clib/stlutil.h"
19
#include "clib/threadhelp.h"
20
#include "clib/weakptr.h"
21
#include "globals/settings.h"
22
#include "plib/systemstate.h"
23

24
#include "../exscrobj.h"
25
#include "../globals/script_internals.h"
26
#include "../globals/state.h"
27
#include "../item/item.h"
28
#include "../mobile/attribute.h"
29
#include "../mobile/charactr.h"
30
#include "../mobile/npc.h"
31
#include "../network/auxclient.h"
32
#include "../network/packethelper.h"
33
#include "../network/packets.h"
34
#include "../network/pktdef.h"
35
#include "../poldbg.h"
36
#include "../polsem.h"
37
#include "../profile.h"
38
#include "../schedule.h"
39
#include "../scrdef.h"
40
#include "../scrsched.h"
41
#include "../scrstore.h"
42
#include "../skills.h"
43
#include "../ufunc.h"
44
#include "../uoexec.h"
45
#include "npcmod.h"
46
#include "uomod.h"
47

48
#include <chrono>
49
#include <module_defs/os.h>
50

51
#ifdef _WIN32
52
#pragma comment( lib, "crypt32.lib" )
53
#endif
54
#include <ctime>
55
#include <curl/curl.h>
56
#include <memory>
57
#include <stdlib.h>
58
#include <string.h>
59
#include <string>
60
#include <string_view>
61
#include <unordered_map>
62

63
// environment variable for mf_GetEnvironmentVariable
64
#ifdef _MSC_VER
65
extern char** _environ;
66
char** environ_vars = _environ;
67
#else
68
extern char** environ;
69
char** environ_vars = environ;
70
#endif
71

72
namespace
73
{
74
class CurlStringList
75
{
76
public:
77
  CurlStringList() : resource_( nullptr ) {}
16✔
78

79
  ~CurlStringList()
16✔
80
  {
81
    if ( resource_ )
16✔
82
    {
83
      curl_slist_free_all( resource_ );
15✔
84
    }
85
  }
16✔
86

87
  void add( const std::string& str )
38✔
88
  {
89
    auto* new_resource = curl_slist_append( resource_, str.c_str() );
38✔
90

91
    if ( new_resource != resource_ && resource_ != nullptr )
38✔
92
    {
93
      curl_slist_free_all( resource_ );
×
94
    }
95

96
    resource_ = new_resource;
38✔
97
  }
38✔
98

99
  curl_slist* get() const { return resource_; }
14✔
100

101
private:
102
  curl_slist* resource_;
103
};
104

105
};  // namespace
106

107

108
namespace Pol::Module
109
{
110
using namespace Bscript;
111

112
unsigned int getnewpid( Core::UOExecutor* uoexec )
281✔
113
{
114
  return Core::scriptScheduler.get_new_pid( uoexec );
281✔
115
}
116
void freepid( unsigned int pid )
281✔
117
{
118
  Core::scriptScheduler.free_pid( pid );
281✔
119
}
281✔
120

121
OSExecutorModule::OSExecutorModule( Bscript::Executor& exec )
281✔
122
    : TmplExecutorModule<OSExecutorModule, Core::PolModule>( exec ),
123
      critical_( false ),
281✔
124
      priority_( Plib::systemstate.config.default_priority ),
281✔
125
      warn_on_runaway_( true ),
281✔
126
      blocked_( false ),
281✔
127
      sleep_until_clock_( 0 ),
281✔
128
      hold_itr_(),
281✔
129
      in_hold_list_( Core::HoldListType::NO_LIST ),
281✔
130
      wait_type( Core::WAIT_TYPE::WAIT_UNKNOWN ),
281✔
131
      pid_( getnewpid( &uoexec() ) ),
281✔
132
      max_eventqueue_size( Core::MAX_EVENTQUEUE_SIZE ),
281✔
133
      events_()
562✔
134
{
135
}
281✔
136

137
OSExecutorModule::~OSExecutorModule()
562✔
138
{
139
  freepid( pid_ );
281✔
140
  pid_ = 0;
281✔
141
  while ( !events_.empty() )
283✔
142
  {
143
    Bscript::BObject ob( events_.front() );
2✔
144
    events_.pop_front();
2✔
145
  }
2✔
146
}
562✔
147

148
unsigned int OSExecutorModule::pid() const
32✔
149
{
150
  return pid_;
32✔
151
}
152

153
BObjectImp* OSExecutorModule::mf_Create_Debug_Context()
×
154
{
155
  return Core::create_debug_context();
×
156
}
157

158
BObjectImp* OSExecutorModule::mf_Debugger()
2✔
159
{
160
  Core::UOExecutor* uoexec;
161
  if ( find_uoexec( pid_, &uoexec ) )
2✔
162
  {
163
    return new BLong( uoexec->attach_debugger() );
2✔
164
  }
NEW
165
  return new BError( "Could not find UOExecutor for current process." );
×
166
}
167

168
BObjectImp* OSExecutorModule::mf_GetProcess()
132✔
169
{
170
  int pid;  // note that while pid's are unsigned, valid values are forced to fit within a signed
171
            // range
172
  if ( !getParam( 0, pid ) )
132✔
173
    return new BError( "Invalid parameter type" );
2✔
174

175
  if ( pid == -1 )
130✔
176
  {
177
    pid = pid_;  // get executor's own pid
62✔
178
  }
179

180
  Core::UOExecutor* uoexec;
181
  if ( find_uoexec( pid, &uoexec ) )
130✔
182
    return new Core::ScriptExObjImp( uoexec );
128✔
183
  return new BError( "Process not found" );
2✔
184
}
185

186
BObjectImp* OSExecutorModule::mf_GetPid()
34✔
187
{
188
  return new BLong( pid_ );
34✔
189
}
190
/*  Ok, this is going to be fun.  In the case where we block,
191
the caller is going to take our return value and push
192
it on the value stack.
193

194
What we'll do is push the value that should be returned
195
if a timeout occurs.  THis way, for timeouts, all we have
196
to do is move the script back into the run queue.
197

198
When we actually complete something, we'll have to
199
pop the value off the stack, and replace it with
200
the real result.
201

202
Whew!
203
*/
204
BObjectImp* OSExecutorModule::mf_Sleep()
13✔
205
{
206
  int nsecs = exec.paramAsLong( 0 );
13✔
207
  SleepFor( nsecs > 0 ? static_cast<u32>( nsecs ) : 1u );
13✔
208
  return new BLong( 0 );
13✔
209
}
210

211
BObjectImp* OSExecutorModule::mf_Sleepms()
36,934,726✔
212
{
213
  int msecs = exec.paramAsLong( 0 );
36,934,726✔
214
  SleepForMs( msecs > 0 ? static_cast<u32>( msecs ) : 1u );
36,934,726✔
215
  return new BLong( 0 );
36,934,726✔
216
}
217

218
BObjectImp* OSExecutorModule::mf_Wait_For_Event()
1,809✔
219
{
220
  if ( !events_.empty() )
1,809✔
221
  {
222
    BObjectImp* imp = events_.front();
420✔
223
    events_.pop_front();
420✔
224
    return imp;
420✔
225
  }
226

227
  auto param = exec.getParamImp( 0 );
1,389✔
228
  double nsecs = 0;
1,389✔
229

230
  if ( auto* long_param = impptrIf<BLong>( param ) )
1,389✔
231
  {
232
    nsecs = long_param->value();
1,384✔
233
    if ( !nsecs )
1,384✔
234
      return new BLong( 0 );
57✔
235
    if ( nsecs < 1 )
1,327✔
NEW
236
      nsecs = 1;
×
237
  }
238
  else if ( auto* double_param = impptrIf<Double>( param ) )
5✔
239
  {
240
    nsecs = double_param->value();
4✔
241
    if ( !nsecs )
4✔
242
      return new BLong( 0 );
1✔
243
    if ( nsecs < 0.01 )
3✔
244
      nsecs = 0.01;
2✔
245
  }
246
  else
247
  {
248
    return new BLong( 0 );
1✔
249
  }
250

251
  wait_type = Core::WAIT_TYPE::WAIT_EVENT;
1,330✔
252
  blocked_ = true;
1,330✔
253
  sleep_until_clock_ =
1,330✔
254
      Core::polclock() + Clib::clamp_convert<u32>( nsecs * Core::POLCLOCKS_PER_SEC );
1,330✔
255
  return new BLong( 0 );
1,330✔
256
}
257

258
BObjectImp* OSExecutorModule::mf_Events_Waiting()
×
259
{
260
  return new BLong( static_cast<int>( events_.size() ) );
×
261
}
262

263
BObjectImp* OSExecutorModule::mf_Start_Script()
27✔
264
{
265
  const String* scriptname_str;
266
  if ( exec.getStringParam( 0, scriptname_str ) )
27✔
267
  {
268
    BObjectImp* imp = exec.getParamImp( 1 );
27✔
269

270
    // FIXME needs to inherit available modules?
271
    Core::ScriptDef sd;
27✔
272
    if ( !sd.config_nodie( scriptname_str->value(), exec.prog()->pkg, "scripts/" ) )
27✔
273
    {
274
      return new BError( "Error in script name" );
×
275
    }
276
    if ( !sd.exists() )
27✔
277
    {
278
      return new BError( "Script " + sd.name() + " does not exist." );
×
279
    }
280
    UOExecutorModule* new_uoemod = Core::start_script( sd, imp->copy() );
27✔
281
    if ( new_uoemod == nullptr )
27✔
282
    {
283
      return new BError( "Unable to start script" );
×
284
    }
285
    UOExecutorModule* this_uoemod = static_cast<UOExecutorModule*>( exec.findModule( "uo" ) );
27✔
286
    if ( new_uoemod != nullptr && this_uoemod != nullptr )
27✔
287
    {
288
      new_uoemod->controller_ = this_uoemod->controller_;
27✔
289
    }
290

291
    return new Core::ScriptExObjImp( &new_uoemod->uoexec() );
27✔
292
  }
27✔
293

NEW
294
  return new BError( "Invalid parameter type" );
×
295
}
296

297

298
BObjectImp* OSExecutorModule::mf_Start_Skill_Script()
×
299
{
300
  Mobile::Character* chr;
301
  const Mobile::Attribute* attr;
302

303
  if ( getCharacterParam( 0, chr ) && getAttributeParam( 1, attr ) )
×
304
  {
305
    if ( !attr->disable_core_checks && !Core::CanUseSkill( chr->client ) )
×
306
      return new BLong( 0 );
×
307

308
    const String* script_name;
NEW
309
    Core::ScriptDef script;
×
310

NEW
311
    if ( exec.getStringParam( 2, script_name ) && ( script_name->length() > 0 ) )
×
312
    {
NEW
313
      if ( !script.config_nodie( script_name->value(), exec.prog()->pkg, "scripts/skills/" ) )
×
314
      {
NEW
315
        return new BError( "Error in script name" );
×
316
      }
NEW
317
      if ( !script.exists() )
×
318
      {
NEW
319
        return new BError( "Script " + script.name() + " does not exist." );
×
320
      }
321
    }
322
    else
323
    {
NEW
324
      if ( !attr->script_.empty() )
×
NEW
325
        script = attr->script_;
×
326
      else
NEW
327
        return new BError( "No script defined for attribute " + attr->name + "." );
×
328
    }
329

330
    ref_ptr<EScriptProgram> prog = find_script2(
331
        script, true,
NEW
332
        /* complain if not found */ Plib::systemstate.config.cache_interactive_scripts );
×
333

NEW
334
    if ( prog.get() != nullptr )
×
335
    {
NEW
336
      BObjectImp* imp = exec.getParamImp( 3 );
×
NEW
337
      if ( imp )
×
338
      {
NEW
339
        if ( chr->start_script( prog.get(), true, imp->copy() ) )
×
340
        {
NEW
341
          if ( chr->hidden() && attr->unhides )
×
NEW
342
            chr->unhide();
×
NEW
343
          if ( attr->delay_seconds )
×
NEW
344
            chr->disable_skills_until( Core::poltime() + attr->delay_seconds );
×
345
        }
346
      }
347
      else
348
      {
NEW
349
        if ( chr->start_script( prog.get(), true ) )
×
350
        {
NEW
351
          if ( chr->hidden() && attr->unhides )
×
NEW
352
            chr->unhide();
×
NEW
353
          if ( attr->delay_seconds )
×
NEW
354
            chr->disable_skills_until( Core::poltime() + attr->delay_seconds );
×
355
        }
356
      }
357
    }
358
    else
359
    {
NEW
360
      std::string msg = "Unable to start skill script:";
×
NEW
361
      msg += script.c_str();
×
NEW
362
      Core::send_sysmessage( chr->client, msg.c_str() );
×
363

NEW
364
      return new BLong( 0 );
×
NEW
365
    }
×
NEW
366
    return new BLong( 1 );
×
UNCOV
367
  }
×
368

NEW
369
  return new BError( "Invalid parameter type" );
×
370
}
371

372
BObjectImp* OSExecutorModule::mf_Set_Critical()
10✔
373
{
374
  int crit;
375
  if ( exec.getParam( 0, crit ) )
10✔
376
  {
377
    critical_ = ( crit != 0 );
10✔
378
    return new BLong( 1 );
10✔
379
  }
380

NEW
381
  return new BError( "Invalid parameter type" );
×
382
}
383

384
BObjectImp* OSExecutorModule::mf_Is_Critical()
×
385
{
386
  if ( critical_ )
×
387
    return new BLong( 1 );
×
NEW
388
  return new BLong( 0 );
×
389
}
390

391
BObjectImp* OSExecutorModule::mf_Run_Script_To_Completion()
9✔
392
{
393
  const char* scriptname = exec.paramAsString( 0 );
9✔
394
  if ( scriptname == nullptr )
9✔
395
    return new BLong( 0 );
×
396

397
  // FIXME needs to inherit available modules?
398
  Core::ScriptDef script;
9✔
399

400
  if ( !script.config_nodie( scriptname, exec.prog()->pkg, "scripts/" ) )
9✔
401
    return new BError( "Script descriptor error" );
×
402

403
  if ( !script.exists() )
9✔
404
    return new BError( "Script does not exist" );
×
405

406
  return Core::run_script_to_completion( script, getParamImp( 1 ) );
9✔
407
}
9✔
408

409
BObjectImp* OSExecutorModule::mf_Run_Script()
26✔
410
{
411
  UOExecutorModule* this_uoemod = static_cast<UOExecutorModule*>( exec.findModule( "uo" ) );
26✔
412
  Core::UOExecutor& this_uoexec = uoexec();
26✔
413

414

415
  if ( this_uoexec.pChild == nullptr )
26✔
416
  {
417
    const String* scriptname_str;
418
    if ( exec.getStringParam( 0, scriptname_str ) )
13✔
419
    {
420
      BObjectImp* imp = exec.getParamImp( 1 );
13✔
421

422
      // FIXME needs to inherit available modules?
423
      Core::ScriptDef sd;
13✔
424
      if ( !sd.config_nodie( scriptname_str->value(), exec.prog()->pkg, "scripts/" ) )
13✔
425
      {
426
        return new BError( "Error in script name" );
×
427
      }
428
      if ( !sd.exists() )
13✔
429
      {
430
        return new BError( "Script " + sd.name() + " does not exist." );
×
431
      }
432
      UOExecutorModule* new_uoemod = Core::start_script( sd, imp->copy() );
13✔
433
      if ( new_uoemod == nullptr )
13✔
434
      {
435
        return new BError( "Unable to run script" );
×
436
      }
437
      if ( new_uoemod )
13✔
438
      {
439
        new_uoemod->controller_ = this_uoemod->controller_;
13✔
440
      }
441
      Core::UOExecutor& new_uoexec = new_uoemod->uoexec();
13✔
442
      //      OSExecutorModule* osemod = uoexec->os_module;
443
      new_uoexec.pParent = &this_uoexec;
13✔
444
      this_uoexec.pChild = &new_uoexec;
13✔
445

446
      // we want to forcefully do this instruction over again:
447
      this_uoexec.PC--;  // run_script(
13✔
448
      this_uoexec.ValueStack.push_back(
13✔
449
          BObjectRef( new BObject( UninitObject::create() ) ) );  //   script_name,
26✔
450
      // No need to push on "param" since the new BLong(0) below will take care of it.//   param )
451

452
      // Put me on hold until my child is done.
453
      suspend();
13✔
454

455
      return new BLong( 0 );
13✔
456
    }
13✔
457

NEW
458
    return new BError( "Invalid parameter type" );
×
459
  }
460

461
  // else I am running a child script, and its ended
462
  BObjectImp* ret;
463

464
  if ( this_uoexec.pChild->ValueStack.empty() )
13✔
465
    ret = new BLong( 1 );
8✔
466
  else
467
    ret = this_uoexec.pChild->ValueStack.back().get()->impptr()->copy();
5✔
468

469
  this_uoexec.pChild->pParent = nullptr;
13✔
470
  this_uoexec.pChild = nullptr;
13✔
471

472
  return ret;
13✔
473
}
474

475
BObjectImp* OSExecutorModule::mf_Set_Debug()
×
476
{
477
  int dbg;
478
  if ( getParam( 0, dbg ) )
×
479
  {
480
    if ( dbg )
×
481
      exec.setDebugLevel( Executor::SOURCELINES );
×
482
    return new BLong( 1 );
×
483
  }
484

NEW
485
  return new BError( "Invalid parameter type" );
×
486
}
487

488
BObjectImp* OSExecutorModule::mf_SysLog()
1,594✔
489
{
490
  BObjectImp* imp = exec.getParamImp( 0 );
1,594✔
491
  int log_verbose;
492
  const String* color;
493
  if ( !exec.getParam( 1, log_verbose ) || !exec.getStringParam( 2, color ) )
1,594✔
494
    return new BError( "Invalid parameter type" );
×
495
  std::string strval = imp->getStringRep();
1,594✔
496
  if ( log_verbose )
1,594✔
497
  {
498
    POLLOGLN( "[{}]: {}", exec.scriptname(), strval );
×
499
    if ( Plib::systemstate.config.enable_colored_output && color->length() )
×
500
    {
501
      INFO_PRINTLN( "{}syslog [{}]: {}{}", color->value(), exec.scriptname(), strval,
×
502
                    Clib::Logging::CONSOLE_RESET_COLOR );
503
    }
504
    else
505
    {
506
      INFO_PRINTLN( "syslog [{}]: {}", exec.scriptname(), strval );
×
507
    }
508
  }
509
  else
510
  {
511
    if ( Plib::systemstate.config.enable_colored_output && color->length() )
1,594✔
512
    {
513
      POLLOGLN( strval );
776✔
514
      INFO_PRINTLN( "{}{}{}", color->value(), strval, Clib::Logging::CONSOLE_RESET_COLOR );
776✔
515
    }
516
    else
517
    {
518
      POLLOG_INFOLN( strval );
818✔
519
    }
520
  }
521
  return new BLong( 1 );
1,594✔
522
}
1,594✔
523

524
BObjectImp* OSExecutorModule::mf_Set_Priority()
×
525
{
526
  int newpri;
527
  if ( getParam( 0, newpri, 1, 255 ) )
×
528
  {
529
    int oldpri = priority_;
×
530
    priority_ = static_cast<unsigned char>( newpri );
×
531
    return new BLong( oldpri );
×
532
  }
533

NEW
534
  return new BError( "Invalid parameter type" );
×
535
}
536

537

538
BObjectImp* OSExecutorModule::mf_Unload_Scripts()
×
539
{
540
  const String* str;
541
  if ( getStringParam( 0, str ) )
×
542
  {
543
    int n;
544
    if ( str->length() == 0 )
×
545
      n = Core::unload_all_scripts();
×
546
    else
547
      n = Core::unload_script( str->data() );
×
548
    return new BLong( n );
×
549
  }
550

NEW
551
  return new BError( "Invalid parameter type" );
×
552
}
553

554
BObjectImp* OSExecutorModule::clear_event_queue()  // DAVE
110✔
555
{
556
  while ( !events_.empty() )
130✔
557
  {
558
    BObject ob( events_.front() );
20✔
559
    events_.pop_front();
20✔
560
  }
20✔
561
  return new BLong( 1 );
110✔
562
}
563

564
BObjectImp* OSExecutorModule::mf_Set_Event_Queue_Size()  // DAVE 11/24
16✔
565
{
566
  unsigned short param;
567
  if ( getParam( 0, param ) )
16✔
568
  {
569
    unsigned short oldsize = max_eventqueue_size;
16✔
570
    max_eventqueue_size = param;
16✔
571
    return new BLong( oldsize );
16✔
572
  }
NEW
573
  return new BError( "Invalid parameter type" );
×
574
}
575

576
BObjectImp* OSExecutorModule::mf_OpenURL()
×
577
{
578
  Mobile::Character* chr;
579
  const String* str;
580
  if ( getCharacterParam( 0, chr ) && ( ( str = getStringParam( 1 ) ) != nullptr ) )
×
581
  {
582
    if ( chr->has_active_client() )
×
583
    {
584
      Network::PktHelper::PacketOut<Network::PktOut_A5> msg;
×
585
      unsigned urllen;
586
      const char* url = str->data();
×
587

588
      urllen = static_cast<unsigned int>( strlen( url ) );
×
589
      if ( urllen > URL_MAX_LEN )
×
590
        urllen = URL_MAX_LEN;
×
591

592
      msg->WriteFlipped<u16>( urllen + 4u );
×
593
      msg->Write( url, static_cast<u16>( urllen + 1 ) );
×
594
      msg.Send( chr->client );
×
595
      return new BLong( 1 );
×
596
    }
×
597

NEW
598
    return new BError( "No client attached" );
×
599
  }
600

NEW
601
  return new BError( "Invalid parameter type" );
×
602
}
603

604

605
BObjectImp* OSExecutorModule::mf_OpenConnection()
4✔
606
{
607
  const String* host;
608
  const String* scriptname_str;
609
  BObjectImp* scriptparam;
610
  unsigned short port;
611
  int assume_string_int;
612
  int keep_connection_int;
613
  int ignore_line_breaks_int;
614
  if ( !getStringParam( 0, host ) || !getParam( 1, port ) || !getStringParam( 2, scriptname_str ) ||
8✔
615
       !getParamImp( 3, scriptparam ) || !getParam( 4, assume_string_int ) ||
4✔
616
       !getParam( 5, keep_connection_int ) || !getParam( 6, ignore_line_breaks_int ) )
8✔
617
    return new BError( "Invalid parameter type" );
×
618

619
  // FIXME needs to inherit available modules?
620
  Core::ScriptDef sd;
4✔
621
  if ( !sd.config_nodie( scriptname_str->value(), exec.prog()->pkg, "scripts/" ) )
4✔
622
  {
623
    return new BError( "Error in script name" );
×
624
  }
625
  if ( !sd.exists() )
4✔
626
  {
627
    return new BError( "Script " + sd.name() + " does not exist." );
×
628
  }
629
  Core::UOExecutor& this_uoexec = uoexec();
4✔
630
  if ( !this_uoexec.suspend() )
4✔
631
  {
632
    DEBUGLOGLN(
×
633
        "Script Error in '{}' PC={}: \n"
634
        "\tThe execution of this script can't be blocked!",
635
        this_uoexec.scriptname(), this_uoexec.PC );
×
636
    return new Bscript::BError( "Script can't be blocked" );
×
637
  }
638

639
  weak_ptr<Core::UOExecutor> uoexec_w = this_uoexec.weakptr;
4✔
640
  std::string hostname( host->value() );
4✔
641
  bool assume_string = assume_string_int != 0;
4✔
642
  bool keep_connection = keep_connection_int != 0;
4✔
643
  bool ignore_line_breaks = ignore_line_breaks_int != 0;
4✔
644
  auto* paramobjimp_raw = scriptparam->copy();  // prevent delete
4✔
645
  Core::networkManager.auxthreadpool->push(
8✔
646
      [uoexec_w, sd, hostname, port, paramobjimp_raw, assume_string, keep_connection,
8✔
647
       ignore_line_breaks]()
8✔
648
      {
649
        Clib::Socket s;
4✔
650
        std::unique_ptr<Network::AuxClientThread> client;
4✔
651
        bool success_open = s.open( hostname.c_str(), port );
4✔
652
        {
653
          Core::PolLock lck;
4✔
654
          std::unique_ptr<BObjectImp> paramobjimp( paramobjimp_raw );
4✔
655
          if ( !uoexec_w.exists() )
4✔
656
          {
657
            DEBUGLOGLN( "OpenConnection Script has been destroyed" );
×
658
            s.close();
×
659
            return;
×
660
          }
661
          if ( !success_open )
4✔
662
          {
663
            uoexec_w.get_weakptr()->ValueStack.back().set(
×
664
                new BObject( new BError( "Error connecting to client" ) ) );
×
665
            uoexec_w.get_weakptr()->revive();
×
666
            return;
×
667
          }
668
          uoexec_w.get_weakptr()->ValueStack.back().set( new BObject( new BLong( 1 ) ) );
4✔
669
          uoexec_w.get_weakptr()->revive();
4✔
670
          client = std::make_unique<Network::AuxClientThread>(
4✔
671
              sd, std::move( s ), paramobjimp.release(), assume_string, keep_connection,
4✔
672
              ignore_line_breaks );
8✔
673
        }
4✔
674
        if ( client )
4✔
675
          client->run();
4✔
676
      } );
4✔
677

678
  return new BLong( 0 );
4✔
679
}
4✔
680

681
size_t curlWriteCallback( void* contents, size_t size, size_t nmemb, void* userp )
12✔
682
{
683
  ( static_cast<std::string*>( userp ) )->append( static_cast<char*>( contents ), size * nmemb );
12✔
684
  return size * nmemb;
12✔
685
}
686

687
const int HTTPREQUEST_EXTENDED_RESPONSE = 0x0001;
688

689
struct CurlHeaderData
690
{
691
  std::optional<std::string> statusText;
692
  std::map<std::string, std::string> headers = {};
693
};
694

695
size_t curlReadHeaderCallback( char* buffer, size_t size, size_t nitems, void* userdata )
18✔
696
{
697
  auto headerData = static_cast<CurlHeaderData*>( userdata );
18✔
698
  std::string value( buffer, size * nitems );
18✔
699

700
  // statusText is set after parsing the first header line
701
  if ( !headerData->statusText.has_value() )
18✔
702
  {
703
    std::string statusText = "";
5✔
704
    if ( value.rfind( "HTTP/", 0 ) == 0 )
5✔
705
    {
706
      // Find space after "HTTP/x" (before status code)
707
      size_t pos = value.find( " " );
5✔
708
      if ( pos != std::string::npos )
5✔
709
      {
710
        // Find space after status code (before status text)
711
        pos = value.find( " ", pos + 1 );
5✔
712
        if ( pos != std::string::npos )
5✔
713
        {
714
          statusText = Clib::strtrim( value.substr( pos + 1 ) );
5✔
715
        }
716
      }
717
    }
718
    headerData->statusText.emplace( statusText );
5✔
719
  }
5✔
720
  else
721
  {
722
    size_t pos = value.find( ":" );
13✔
723
    if ( pos != std::string::npos )
13✔
724
    {
725
      std::string headerName = Clib::strlowerASCII( value.substr( 0, pos ) );
8✔
726
      std::string headerValue = Clib::strtrim( value.substr( pos + 1 ) );
8✔
727

728
      // If the header already exists, append it (comma-separated) to previous value
729
      if ( headerData->headers.count( headerName ) )
8✔
730
      {
731
        auto& existing = headerData->headers.at( headerName );
1✔
732
        existing.append( ", " );
1✔
733
        existing.append( headerValue );
1✔
734
      }
735
      else
736
      {
737
        headerData->headers.emplace( headerName, headerValue );
7✔
738
      }
739
    }
8✔
740
  }
741

742
  return nitems * size;
18✔
743
}
18✔
744

745
BObjectImp* OSExecutorModule::mf_HTTPRequest()
18✔
746
{
747
  const String *url, *method;
748
  BObjectImp* options;
749
  int flags;
750
  if ( !getStringParam( 0, url ) || !getStringParam( 1, method ) || !getParamImp( 2, options ) ||
36✔
751
       !getParam( 3, flags ) )
18✔
752
    return new BError( "Invalid parameter type" );
×
753
  Core::UOExecutor& this_uoexec = uoexec();
18✔
754
  weak_ptr<Core::UOExecutor> uoexec_w = this_uoexec.weakptr;
18✔
755

756
  std::shared_ptr<CURL> curl_sp( curl_easy_init(), curl_easy_cleanup );
18✔
757
  CURL* curl = curl_sp.get();
18✔
758
  if ( !curl )
18✔
759
    return new BError( "curl_easy_init() failed" );
×
760
  curl_easy_setopt( curl, CURLOPT_URL, url->data() );
18✔
761
  curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, method->data() );
18✔
762
  curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curlWriteCallback );
18✔
763
  curl_easy_setopt( curl, CURLOPT_ACCEPT_ENCODING,
18✔
764
                    "" );  // allow plaintext and compressed (with automatic deflate)
765

766
  struct curl_slist* chunk = nullptr;
18✔
767
  if ( options->isa( Bscript::BObjectImp::OTStruct ) )
18✔
768
  {
769
    Bscript::BStruct* opts = static_cast<Bscript::BStruct*>( options );
18✔
770
    const BObjectImp* data = opts->FindMember( "data" );
18✔
771
    if ( data != nullptr )
18✔
772
    {
773
      curl_easy_setopt( curl, CURLOPT_COPYPOSTFIELDS, data->getStringRep().c_str() );
×
774
    }
775

776
    const BObjectImp* headers_ = opts->FindMember( "headers" );
18✔
777

778
    if ( headers_ != nullptr && headers_->isa( BObjectImp::OTStruct ) )
18✔
779
    {
780
      const BStruct* headers = static_cast<const BStruct*>( headers_ );
×
781

782
      for ( const auto& content : headers->contents() )
×
783
      {
784
        BObjectImp* ref = content.second->impptr();
×
785
        std::string header = content.first + ": " + ref->getStringRep();
×
786
        chunk = curl_slist_append( chunk, header.c_str() );
×
787
      }
×
788
      curl_easy_setopt( curl, CURLOPT_HTTPHEADER, chunk );
×
789
    }
790
  }
791

792
  if ( !this_uoexec.suspend() )
18✔
793
  {
794
    DEBUGLOGLN(
×
795
        "Script Error in '{}' PC={}: \n"
796
        "\tThe execution of this script can't be blocked!",
797
        this_uoexec.scriptname(), this_uoexec.PC );
×
798
    return new Bscript::BError( "Script can't be blocked" );
×
799
  }
800

801
  Core::networkManager.auxthreadpool->push(
36✔
802
      [uoexec_w, curl_sp, chunk, flags]()
36✔
803
      {
804
        CURL* curl = curl_sp.get();
18✔
805
        CURLcode res;
806
        std::string readBuffer;
18✔
807
        CurlHeaderData headerData;
18✔
808

809
        if ( flags == HTTPREQUEST_EXTENDED_RESPONSE )
18✔
810
        {
811
          curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, curlReadHeaderCallback );
5✔
812
          curl_easy_setopt( curl, CURLOPT_HEADERDATA, &headerData );
5✔
813
        }
814

815
        curl_easy_setopt( curl, CURLOPT_WRITEDATA, &readBuffer );
18✔
816

817
        /* Perform the request, res will get the return code */
818
        res = curl_easy_perform( curl );
18✔
819
        if ( chunk != nullptr )
18✔
820
          curl_slist_free_all( chunk );
×
821
        {
822
          Core::PolLock lck;
18✔
823

824
          if ( !uoexec_w.exists() )
18✔
825
          {
826
            DEBUGLOGLN( "HTTPRequest Script has been destroyed" );
×
827
            return;
×
828
          }
829
          /* Check for errors */
830
          if ( res != CURLE_OK )
18✔
831
          {
832
            uoexec_w.get_weakptr()->ValueStack.back().set(
×
833
                new BObject( new BError( curl_easy_strerror( res ) ) ) );
×
834
          }
835
          else
836
          {
837
            if ( flags == HTTPREQUEST_EXTENDED_RESPONSE )
18✔
838
            {
839
              auto response = std::make_unique<Bscript::BDictionary>();
5✔
840
              auto headers = std::make_unique<Bscript::BDictionary>();
5✔
841
              long http_code = 0;
5✔
842

843
              res = curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &http_code );
5✔
844
              if ( res == CURLE_OK )
5✔
845
              {
846
                response->addMember( new String( "status" ), new BLong( http_code ) );
5✔
847
              }
848
              else
849
              {
850
                response->addMember( new String( "status" ),
×
851
                                     new BError( curl_easy_strerror( res ) ) );
×
852
              }
853

854
              response->addMember( new String( "statusText" ),
10✔
855
                                   new String( headerData.statusText.value_or( "" ) ) );
10✔
856

857
              response->addMember( new String( "body" ), new String( readBuffer ) );
5✔
858

859
              for ( auto const& [key, value] : headerData.headers )
12✔
860
              {
861
                headers->addMember( new String( key ), new String( value ) );
7✔
862
              }
863

864
              response->addMember( new String( "headers" ), headers.release() );
5✔
865

866
              uoexec_w.get_weakptr()->ValueStack.back().set( new BObject( response.release() ) );
5✔
867
            }
5✔
868
            else
869
            {
870
              // TODO: no sanitize happens, optional function param iso/utf8 encoding, or
871
              // parse the header of the http answer?
872
              uoexec_w.get_weakptr()->ValueStack.back().set(
26✔
873
                  new BObject( new String( readBuffer ) ) );
13✔
874
            }
875
          }
876

877
          uoexec_w.get_weakptr()->revive();
18✔
878
        }
18✔
879

880
        /* always cleanup */
881
        // curl_easy_cleanup() is performed when the shared pointer deallocates
882
      } );
18✔
883

884
  return new BLong( 0 );
18✔
885
}
18✔
886

887
// signal_event() takes ownership of the pointer which is passed to it.
888
// Objects must not be touched or deleted after being sent here!
889
// TODO: Find a better way to enforce this in the codebase.
890
bool OSExecutorModule::signal_event( BObjectImp* imp )
1,686✔
891
{
892
  INC_PROFILEVAR( events );
1,686✔
893

894
  if ( blocked_ && ( wait_type == Core::WAIT_TYPE::WAIT_EVENT ) )  // already being waited for
1,686✔
895
  {
896
    /* Now, the tricky part.  The value to return on an error or
897
    completion condition has already been pushed onto the value
898
    stack - so, we need to replace it with the real result.
899
    */
900
    exec.ValueStack.back().set( new BObject( imp ) );
1,243✔
901
    /* This executor will get moved to the run queue at the
902
    next step_scripts(), where blocked is checked looking
903
    for timed out or completed operations. */
904

905
    revive();
1,243✔
906
  }
907
  else  // not being waited for, so queue for later.
908
  {
909
    if ( events_.size() < max_eventqueue_size )
443✔
910
    {
911
      events_.push_back( imp );
442✔
912
    }
913
    else
914
    {
915
      if ( Plib::systemstate.config.discard_old_events )
1✔
916
      {
917
        BObject ob( events_.front() );
×
918
        events_.pop_front();
×
919
        events_.push_back( imp );
×
920
      }
×
921
      else
922
      {
923
        BObject ob( imp );
1✔
924
        if ( Plib::systemstate.config.loglevel >= 11 )
1✔
925
        {
926
          INFO_PRINTLN( "Event queue for {} is full, discarding event.", exec.scriptname() );
1✔
927
          ExecutorModule* em = exec.findModule( "npc" );
1✔
928
          if ( em )
1✔
929
          {
930
            NPCExecutorModule* npcemod = static_cast<NPCExecutorModule*>( em );
1✔
931
            INFO_PRINTLN( "NPC Serial: {:#x}{}", npcemod->controlled_npc().serial,
1✔
932
                          npcemod->controlled_npc().pos() );
1✔
933
          }
934

935
          INFO_PRINTLN( "Event: {}", ob->getStringRep() );
1✔
936
        }
937
        return false;  // Event-queue is full
1✔
938
      }
1✔
939
    }
940
  }
941

942
  return true;  // Event was successfully sent (perhaps by discarding old events)
1,685✔
943
}
944

945
void OSExecutorModule::SleepFor( u32 nsecs )
13✔
946
{
947
  if ( !nsecs )
13✔
948
    return;
×
949
  blocked_ = true;
13✔
950
  wait_type = Core::WAIT_TYPE::WAIT_SLEEP;
13✔
951
  sleep_until_clock_ = Core::polclock() + nsecs * Core::POLCLOCKS_PER_SEC;
13✔
952
}
953

954
void OSExecutorModule::SleepForMs( u32 msecs )
36,934,727✔
955
{
956
  if ( !msecs )
36,934,727✔
957
    return;
×
958
  blocked_ = true;
36,934,727✔
959
  wait_type = Core::WAIT_TYPE::WAIT_SLEEP;
36,934,727✔
960
  sleep_until_clock_ = Core::polclock() + msecs * Core::POLCLOCKS_PER_SEC / 1000;
36,934,727✔
961
  if ( !sleep_until_clock_ )
36,934,727✔
962
    sleep_until_clock_ = 1;
2✔
963
}
964

965
void OSExecutorModule::suspend()
433✔
966
{
967
  blocked_ = true;
433✔
968
  wait_type = Core::WAIT_TYPE::WAIT_SLEEP;
433✔
969
  sleep_until_clock_ = 0;  // wait forever
433✔
970
}
433✔
971

972
void OSExecutorModule::revive()
36,936,866✔
973
{
974
  blocked_ = false;
36,936,866✔
975
  if ( in_hold_list_ == Core::HoldListType::TIMEOUT_LIST )
36,936,866✔
976
  {
977
    in_hold_list_ = Core::HoldListType::NO_LIST;
36,936,064✔
978
    Core::scriptScheduler.revive_timeout( &uoexec(), hold_itr_ );
36,936,064✔
979
  }
980
  else if ( in_hold_list_ == Core::HoldListType::NOTIMEOUT_LIST )
802✔
981
  {
982
    in_hold_list_ = Core::HoldListType::NO_LIST;
796✔
983
    Core::scriptScheduler.revive_notimeout( &uoexec() );
796✔
984
  }
985
  else if ( in_hold_list_ == Core::HoldListType::DEBUGGER_LIST )
6✔
986
  {
987
    // stays right where it is.
988
  }
989
}
36,936,866✔
990
bool OSExecutorModule::in_debugger_holdlist() const
108✔
991
{
992
  return ( in_hold_list_ == Core::HoldListType::DEBUGGER_LIST );
108✔
993
}
994
void OSExecutorModule::revive_debugged()
9✔
995
{
996
  in_hold_list_ = Core::HoldListType::NO_LIST;
9✔
997
  Core::scriptScheduler.revive_debugged( &uoexec() );
9✔
998
}
9✔
999

1000
bool OSExecutorModule::critical() const
111,230,038✔
1001
{
1002
  return critical_;
111,230,038✔
1003
}
1004
void OSExecutorModule::critical( bool critical )
×
1005
{
1006
  critical_ = critical;
×
1007
}
×
1008

1009
bool OSExecutorModule::warn_on_runaway() const
24✔
1010
{
1011
  return warn_on_runaway_;
24✔
1012
}
1013
void OSExecutorModule::warn_on_runaway( bool warn_on_runaway )
×
1014
{
1015
  warn_on_runaway_ = warn_on_runaway;
×
1016
}
×
1017

1018
unsigned char OSExecutorModule::priority() const
36,978,654✔
1019
{
1020
  return priority_;
36,978,654✔
1021
}
1022
void OSExecutorModule::priority( unsigned char priority )
14✔
1023
{
1024
  priority_ = priority;
14✔
1025
}
14✔
1026

1027
Core::polclock_t OSExecutorModule::sleep_until_clock() const
221,611,754✔
1028
{
1029
  return sleep_until_clock_;
221,611,754✔
1030
}
1031
void OSExecutorModule::sleep_until_clock( Core::polclock_t sleep_until_clock )
×
1032
{
1033
  sleep_until_clock_ = sleep_until_clock;
×
1034
}
×
1035

1036
Core::TimeoutHandle OSExecutorModule::hold_itr() const
×
1037
{
1038
  return hold_itr_;
×
1039
}
1040
void OSExecutorModule::hold_itr( Core::TimeoutHandle hold_itr )
36,936,070✔
1041
{
1042
  hold_itr_ = hold_itr;
36,936,070✔
1043
}
36,936,070✔
1044

1045
Core::HoldListType OSExecutorModule::in_hold_list() const
36,937,067✔
1046
{
1047
  return in_hold_list_;
36,937,067✔
1048
}
1049
void OSExecutorModule::in_hold_list( Core::HoldListType in_hold_list )
36,936,875✔
1050
{
1051
  in_hold_list_ = in_hold_list;
36,936,875✔
1052
}
36,936,875✔
1053

1054
const int SCRIPTOPT_NO_INTERRUPT = 1;
1055
const int SCRIPTOPT_DEBUG = 2;
1056
const int SCRIPTOPT_NO_RUNAWAY = 3;
1057
const int SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES = 4;
1058
const int SCRIPTOPT_AUXSVC_ASSUME_STRING = 5;
1059
const int SCRIPTOPT_SURVIVE_ATTACHED_DISCONNECT = 6;
1060

1061
BObjectImp* OSExecutorModule::mf_Set_Script_Option()
×
1062
{
1063
  int optnum;
1064
  int optval;
1065
  if ( getParam( 0, optnum ) && getParam( 1, optval ) )
×
1066
  {
1067
    int oldval;
1068
    switch ( optnum )
×
1069
    {
1070
    case SCRIPTOPT_NO_INTERRUPT:
×
1071
      oldval = critical_ ? 1 : 0;
×
1072
      critical_ = optval ? true : false;
×
1073
      break;
×
1074
    case SCRIPTOPT_DEBUG:
×
1075
      oldval = exec.getDebugLevel();
×
1076
      if ( optval )
×
1077
        exec.setDebugLevel( Executor::SOURCELINES );
×
1078
      else
1079
        exec.setDebugLevel( Executor::NONE );
×
1080
      break;
×
1081
    case SCRIPTOPT_NO_RUNAWAY:
×
1082
      oldval = warn_on_runaway_ ? 1 : 0;
×
1083
      warn_on_runaway_ = !optval;
×
1084
      break;
×
1085
    case SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES:
×
1086
    {
1087
      Core::UOExecutor& uoex = uoexec();
×
1088
      oldval = uoex.can_access_offline_mobiles_ ? 1 : 0;
×
1089
      uoex.can_access_offline_mobiles_ = optval ? true : false;
×
1090
    }
1091
    break;
×
1092
    case SCRIPTOPT_AUXSVC_ASSUME_STRING:
×
1093
    {
1094
      Core::UOExecutor& uoex = uoexec();
×
1095
      oldval = uoex.auxsvc_assume_string ? 1 : 0;
×
1096
      uoex.auxsvc_assume_string = optval ? true : false;
×
1097
    }
1098
    break;
×
1099
    case SCRIPTOPT_SURVIVE_ATTACHED_DISCONNECT:
×
1100
    {
1101
      Core::UOExecutor& uoex = uoexec();
×
1102
      oldval = uoex.survive_attached_disconnect ? 1 : 0;
×
1103
      uoex.survive_attached_disconnect = optval ? true : false;
×
1104
    }
1105
    break;
×
1106
    default:
×
1107
      return new BError( "Unknown Script Option" );
×
1108
    }
1109
    return new BLong( oldval );
×
1110
  }
1111

NEW
1112
  return new BError( "Invalid parameter type" );
×
1113
}
1114

1115
BObjectImp* OSExecutorModule::mf_Clear_Event_Queue()  // DAVE
110✔
1116
{
1117
  return ( clear_event_queue() );
110✔
1118
}
1119

1120
namespace
1121
{
1122
struct ScriptDiffData
1123
{
1124
  std::string name;
1125
  u64 instructions;
1126
  u64 pid;
1127
  ScriptDiffData( Core::UOExecutor* ex )
×
1128
      : name( ex->scriptname() ), instructions( ex->instr_cycles ), pid( ex->pid() )
×
1129
  {
1130
  }
×
1131
  ScriptDiffData( Core::UOExecutor* ex, u64 instr ) : ScriptDiffData( ex )
×
1132
  {
1133
    instructions -= instr;
×
1134
    auto uoemod = static_cast<Module::UOExecutorModule*>( ex->findModule( "uo" ) );
×
1135
    if ( uoemod->attached_chr_ != nullptr )
×
1136
      name += " (" + uoemod->attached_chr_->name() + ")";
×
1137
    else if ( uoemod->attached_npc_ != nullptr )
×
1138
      name += " (" + static_cast<Mobile::NPC*>( uoemod->attached_npc_ )->templatename() + ")";
×
1139
    else if ( uoemod->attached_item_.get() )
×
1140
      name += " (" + uoemod->attached_item_->name() + ")";
×
1141
  }
×
1142

1143
  bool operator>( const ScriptDiffData& other ) const { return instructions > other.instructions; }
×
1144
};
1145
struct PerfData
1146
{
1147
  std::unordered_map<u64, ScriptDiffData> data;
1148
  weak_ptr<Core::UOExecutor> uoexec_w;
1149
  size_t max_scripts;
1150
  PerfData( weak_ptr<Core::UOExecutor> weak_ex, size_t max_count )
×
1151
      : data(), uoexec_w( weak_ex ), max_scripts( max_count )
×
1152
  {
1153
  }
×
1154
  static void collect_perf( PerfData* data_ptr )
×
1155
  {
1156
    std::unique_ptr<PerfData> data( data_ptr );
×
1157
    std::vector<ScriptDiffData> res;
×
1158
    if ( !data->uoexec_w.exists() )
×
1159
    {
1160
      DEBUGLOGLN( "PerformanceMeasure Script has been destroyed" );
×
1161
      return;
×
1162
    }
1163
    double sum_instr( 0 );
×
1164
    const auto& runlist = Core::scriptScheduler.getRunlist();
×
1165
    const auto& ranlist = Core::scriptScheduler.getRanlist();
×
1166
    const auto& holdlist = Core::scriptScheduler.getHoldlist();
×
1167
    const auto& notimeoutholdlist = Core::scriptScheduler.getNoTimeoutHoldlist();
×
1168
    auto collect = [&]( Core::UOExecutor* scr )
×
1169
    {
1170
      auto itr = data->data.find( scr->pid() );
×
1171
      if ( itr == data->data.end() )
×
1172
        return;
×
1173
      res.emplace_back( scr, itr->second.instructions );
×
1174
      sum_instr += res.back().instructions;
×
1175
    };
×
1176
    for ( const auto& scr : runlist )
×
1177
      collect( scr );
×
1178
    for ( const auto& scr : ranlist )
×
1179
      collect( scr );
×
1180
    for ( const auto& scr : holdlist )
×
1181
      collect( scr.second );
×
1182
    for ( const auto& scr : notimeoutholdlist )
×
1183
      collect( scr );
×
1184
    std::sort( res.begin(), res.end(), std::greater<ScriptDiffData>() );
×
1185

1186
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
1187
    for ( size_t i = 0; i < res.size() && i < data->max_scripts; ++i )
×
1188
    {
1189
      std::unique_ptr<BStruct> elem( new BStruct );
×
1190
      elem->addMember( "name", new String( res[i].name ) );
×
1191
      elem->addMember( "instructions", new Double( static_cast<double>( res[i].instructions ) ) );
×
1192
      elem->addMember( "pid", new BLong( static_cast<int>( res[i].pid ) ) );
×
1193
      elem->addMember( "percent", new Double( res[i].instructions / sum_instr * 100.0 ) );
×
1194
      arr->addElement( elem.release() );
×
1195
    }
×
1196
    std::unique_ptr<BStruct> result( new BStruct );
×
1197
    result->addMember( "scripts", arr.release() );
×
1198
    result->addMember( "total_number_observed", new BLong( static_cast<int>( res.size() ) ) );
×
1199
    result->addMember( "total_instructions", new Double( sum_instr ) );
×
1200
    data->uoexec_w.get_weakptr()->ValueStack.back().set( new BObject( result.release() ) );
×
1201

1202
    data->uoexec_w.get_weakptr()->revive();
×
1203
  }
×
1204
};
1205

1206
}  // namespace
1207

1208
BObjectImp* OSExecutorModule::mf_PerformanceMeasure()
×
1209
{
1210
  int second_delta, max_scripts;
1211
  if ( !getParam( 0, second_delta ) || !getParam( 1, max_scripts ) )
×
1212
    return new BError( "Invalid parameter type" );
×
1213

1214
  auto& this_uoexec = uoexec();
×
1215

1216
  if ( !this_uoexec.suspend() )
×
1217
  {
1218
    DEBUGLOGLN(
×
1219
        "Script Error in '{}' PC={}: \n"
1220
        "\tThe execution of this script can't be blocked!",
1221
        this_uoexec.scriptname(), this_uoexec.PC );
×
1222
    return new Bscript::BError( "Script can't be blocked" );
×
1223
  }
1224

1225
  const auto& runlist = Core::scriptScheduler.getRunlist();
×
1226
  const auto& ranlist = Core::scriptScheduler.getRanlist();
×
1227
  const auto& holdlist = Core::scriptScheduler.getHoldlist();
×
1228
  const auto& notimeoutholdlist = Core::scriptScheduler.getNoTimeoutHoldlist();
×
1229

1230
  std::unique_ptr<PerfData> perf( new PerfData( this_uoexec.weakptr, max_scripts ) );
×
1231
  for ( const auto& scr : runlist )
×
1232
    perf->data.insert( std::make_pair( scr->pid(), ScriptDiffData( scr ) ) );
×
1233
  for ( const auto& scr : ranlist )
×
1234
    perf->data.insert( std::make_pair( scr->pid(), ScriptDiffData( scr ) ) );
×
1235
  for ( const auto& scr : holdlist )
×
1236
    perf->data.insert( std::make_pair( scr.second->pid(), ScriptDiffData( scr.second ) ) );
×
1237
  for ( const auto& scr : notimeoutholdlist )
×
1238
    perf->data.insert( std::make_pair( scr->pid(), ScriptDiffData( scr ) ) );
×
1239

1240
  new Core::OneShotTaskInst<PerfData*>( nullptr,
×
1241
                                        Core::polclock() + second_delta * Core::POLCLOCKS_PER_SEC,
×
1242
                                        PerfData::collect_perf, perf.release() );
×
1243

1244
  return new BLong( 0 );  // dummy
×
1245
}
×
1246

1247
BObjectImp* OSExecutorModule::mf_LoadExportedScript()
178✔
1248
{
1249
  Core::UOExecutor& this_uoexec = uoexec();
178✔
1250
  if ( this_uoexec.pChild == nullptr )
178✔
1251
  {
1252
    const String* scriptname_str;
1253
    ObjArray* arr;
1254
    if ( !exec.getStringParam( 0, scriptname_str ) || !getObjArrayParam( 1, arr ) )
90✔
1255
      return new BError( "Invalid parameter type" );
×
1256
    Core::ScriptDef sd;
90✔
1257
    if ( !sd.config_nodie( scriptname_str->value(), exec.prog()->pkg, "scripts/" ) )
90✔
1258
      return new BError( "Error in script name" );
×
1259
    if ( !sd.exists() )
90✔
1260
      return new BError( "Script " + sd.name() + " does not exist." );
×
1261
    ref_ptr<Bscript::EScriptProgram> program = find_script2( sd );
90✔
1262
    if ( program.get() == nullptr )
90✔
1263
    {
1264
      ERROR_PRINTLN( "Error reading script {}", sd.name() );
×
1265
      return new Bscript::BError( "Unable to read script" );
×
1266
    }
1267
    Core::UOExecutor* uoexec = Core::create_script_executor();
90✔
1268
    uoexec->keep_alive( true );
90✔
1269
    Core::add_common_exmods( *uoexec );
90✔
1270
    uoexec->addModule( new Module::UOExecutorModule( *uoexec ) );
90✔
1271

1272
    uoexec->setProgram( program.get() );
90✔
1273
    if ( program->haveProgram )
90✔
1274
    {
1275
      for ( int i = (int)( arr->ref_arr.size() ) - 1; i >= 0; --i )
92✔
1276
        uoexec->pushArg( arr->ref_arr[i].get()->impptr() );
2✔
1277
    }
1278
    if ( this_uoexec.critical() )  // execute directy
90✔
1279
    {
1280
      uoexec->exec();
2✔
1281
      BObjectImp* ret;
1282
      if ( uoexec->error() )
2✔
1283
        ret = new BLong( 0 );
×
1284
      else if ( uoexec->ValueStack.empty() )
2✔
1285
        ret = new BLong( 1 );
×
1286
      else
1287
        ret = uoexec->ValueStack.back().get()->impptr()->copy();
2✔
1288
      uoexec->set_running_to_completion( false );
2✔
1289
      uoexec->suspend();
2✔
1290
      Core::scriptScheduler.schedule( uoexec );
2✔
1291

1292
      auto array = std::make_unique<Bscript::ObjArray>();
2✔
1293
      array->addElement( new Core::ExportScriptObjImp( uoexec ) );
2✔
1294
      array->addElement( ret );
2✔
1295

1296
      return array.release();
2✔
1297
    }
2✔
1298

1299
    Core::scriptScheduler.schedule( uoexec );
88✔
1300

1301
    uoexec->pParent = &this_uoexec;
88✔
1302
    this_uoexec.pChild = uoexec;
88✔
1303

1304
    this_uoexec.PC--;
88✔
1305
    // need to fill the valuestack equal to param count (-the return value)
1306
    this_uoexec.ValueStack.push_back( BObjectRef( new BObject( UninitObject::create() ) ) );
88✔
1307
    suspend();
88✔
1308

1309
    return UninitObject::create();
88✔
1310
  }
90✔
1311
  // reentry
1312
  BObjectImp* ret;
1313
  if ( this_uoexec.pChild->error() )
88✔
NEW
1314
    ret = new BLong( 0 );
×
1315
  else if ( this_uoexec.pChild->ValueStack.empty() )
88✔
1316
    ret = new BLong( 1 );
7✔
1317
  else
1318
  {
1319
    ret = this_uoexec.pChild->ValueStack.back().get()->impptr()->copy();
81✔
1320
    this_uoexec.pChild->ValueStack.pop_back();
81✔
1321
  }
1322
  auto array = std::make_unique<Bscript::ObjArray>();
88✔
1323
  array->addElement( new Core::ExportScriptObjImp( this_uoexec.pChild ) );
88✔
1324
  array->addElement( ret );
88✔
1325

1326
  this_uoexec.pChild->pParent = nullptr;
88✔
1327
  this_uoexec.pChild = nullptr;
88✔
1328

1329
  return array.release();
88✔
1330
}
88✔
1331

1332
BObjectImp* OSExecutorModule::mf_GetEnvironmentVariable()
52✔
1333
{
1334
  const auto& allowed_vars = Plib::systemstate.config.allowed_environmentvariables_access;
52✔
1335
  if ( allowed_vars.empty() )
52✔
1336
    return new BError( "Environment Variable access disallowed due to pol.cfg setting" );
×
1337
  const String* env_name;
1338
  if ( !exec.getStringParam( 0, env_name ) )
52✔
1339
    return new BError( "Invalid parameter type" );
×
1340

1341
  bool all_allowed = allowed_vars.size() == 1 && allowed_vars[0] == "*";
52✔
1342

1343
  if ( env_name->length() == 0 )
52✔
1344
  {
1345
    auto envs = std::make_unique<Bscript::BDictionary>();
1✔
1346
    for ( char** current = environ_vars; *current; ++current )
122✔
1347
    {
1348
      std::string_view env( *current );
121✔
1349
      size_t pos = env.find_first_of( "=" );
121✔
1350
      if ( pos == std::string_view::npos )
121✔
1351
        continue;
×
1352
      auto key = env.substr( 0, pos );
121✔
1353
      auto key_lowered = Clib::strlowerASCII( std::string{ key } );
121✔
1354
      auto val = env.substr( pos + 1 );
121✔
1355

1356
      if ( all_allowed || std::find( allowed_vars.begin(), allowed_vars.end(), key_lowered ) !=
363✔
1357
                              allowed_vars.end() )
363✔
1358
      {
1359
        envs->addMember( new String( key, String::Tainted::YES ),
10✔
1360
                         new String( val, String::Tainted::YES ) );
5✔
1361
      }
1362
    }
121✔
1363
    return envs.release();
1✔
1364
  }
1✔
1365

1366
  if ( !all_allowed )
51✔
1367
  {
1368
    auto name_lowered = Clib::strlowerASCII( env_name->value() );
51✔
1369
    if ( std::find( allowed_vars.begin(), allowed_vars.end(), name_lowered ) == allowed_vars.end() )
51✔
1370
      return new BError( "Environment Variable access disallowed due to pol.cfg setting" );
2✔
1371
  }
51✔
1372

1373
  const char* env_val = std::getenv( env_name->data() );
49✔
1374
  if ( !env_val )
49✔
1375
    return new BError( "Environment variable not found" );
3✔
1376
  return new String( env_val, String::Tainted::YES );
46✔
1377
}
1378

1379
BObjectImp* OSExecutorModule::mf_SendEmail()
9✔
1380
{
1381
  if ( Core::settingsManager.email_cfg.url.empty() )
9✔
1382
    return new BError( "Email settings are not configured" );
×
1383

1384

1385
  const String *from, *subject, *body, *contentType;
1386
  BObjectImp *recipient, *bcc;
1387

1388
  if ( !( getStringParam( 0, from ) && getParamImp( 1, recipient ) &&
17✔
1389
          getStringParam( 2, subject ) && getStringParam( 3, body ) && getParamImp( 4, bcc ) &&
8✔
1390
          getStringParam( 5, contentType ) ) )
8✔
1391
  {
1392
    return new BError( "Invalid parameter type" );
1✔
1393
  }
1394

1395
  std::shared_ptr<CURL> curl_sp( curl_easy_init(), curl_easy_cleanup );
8✔
1396
  CURL* curl = curl_sp.get();
8✔
1397
  if ( !curl )
8✔
1398
  {
1399
    return new BError( "curl_easy_init() failed" );
×
1400
  }
1401

1402
  auto headers_slist = std::make_shared<CurlStringList>();
8✔
1403
  auto recipients_slist = std::make_shared<CurlStringList>();
8✔
1404
  std::string to_header_value;
8✔
1405

1406
  auto extract_email_address = []( const String* email_string )
18✔
1407
  {
1408
    const auto& input = email_string->value();
18✔
1409
    auto start = input.find( '<' );
18✔
1410
    auto end = input.find( '>' );
18✔
1411

1412
    if ( start != std::string::npos && end != std::string::npos && end > start + 1 )
18✔
1413
    {
1414
      return input.substr( start + 1, end - start - 1 );
3✔
1415
    }
1416

1417
    // No angle brackets: assume the whole input is the email address
1418
    return input;
15✔
1419
  };
1420

1421
  auto handle_recipients = [&]( BObjectImp* string_or_array, bool is_bcc ) -> BObjectImp*
16✔
1422
  {
1423
    // The `to` or `bcc` can be falsey to specify no recipient for this field.
1424
    if ( !string_or_array->isTrue() )
16✔
1425
      return nullptr;
8✔
1426

1427
    if ( auto* recipient_string = impptrIf<String>( string_or_array ) )
8✔
1428
    {
1429
      if ( !is_bcc )
5✔
1430
      {
1431
        if ( !to_header_value.empty() )
4✔
1432
          to_header_value += ", ";
×
1433

1434
        to_header_value += recipient_string->data();
4✔
1435
      }
1436

1437
      recipients_slist->add( extract_email_address( recipient_string ) );
5✔
1438
    }
1439
    else if ( auto* recipient_array = impptrIf<ObjArray>( string_or_array ) )
3✔
1440
    {
1441
      for ( const auto& elem : recipient_array->ref_arr )
8✔
1442
      {
1443
        if ( auto* this_recipient = elem->impptr_if<String>() )
6✔
1444
        {
1445
          // Bcc is not added to the header, and only included in CURLOPT_MAIL_RCPT
1446
          if ( !is_bcc )
5✔
1447
          {
1448
            if ( !to_header_value.empty() )
2✔
1449
              to_header_value += ", ";
1✔
1450

1451
            to_header_value += this_recipient->data();
2✔
1452
          }
1453

1454
          recipients_slist->add( extract_email_address( this_recipient ) );
5✔
1455
        }
1456
        else
1457
        {
1458
          return new BError( "Invalid recipient type in array" );
1✔
1459
        }
1460
      }
1461
    }
1462
    else
1463
    {
1464
      return new BError( "Invalid recipient type" );
×
1465
    }
1466

1467
    return nullptr;
7✔
1468
  };
8✔
1469

1470
  if ( auto res = curl_easy_setopt( curl, CURLOPT_URL, Core::settingsManager.email_cfg.url.data() );
8✔
1471
       res != CURLE_OK )
1472
  {
1473
    return new BError( "curl_easy_setopt(CURLOPT_URL) failed: " +
×
1474
                       std::string( curl_easy_strerror( res ) ) );
×
1475
  }
1476

1477
  if ( !Core::settingsManager.email_cfg.username.empty() )
8✔
1478
  {
1479
    if ( auto res = curl_easy_setopt( curl, CURLOPT_USERNAME,
8✔
1480
                                      Core::settingsManager.email_cfg.username.c_str() );
1481
         res != CURLE_OK )
1482
    {
1483
      return new BError( "curl_easy_setopt(CURLOPT_USERNAME) failed: " +
×
1484
                         std::string( curl_easy_strerror( res ) ) );
×
1485
    }
1486
  }
1487

1488
  if ( !Core::settingsManager.email_cfg.password.empty() )
8✔
1489
  {
1490
    if ( auto res = curl_easy_setopt( curl, CURLOPT_PASSWORD,
8✔
1491
                                      Core::settingsManager.email_cfg.password.c_str() );
1492
         res != CURLE_OK )
1493
    {
1494
      return new BError( "curl_easy_setopt(CURLOPT_PASSWORD) failed: " +
×
1495
                         std::string( curl_easy_strerror( res ) ) );
×
1496
    }
1497
  }
1498

1499
  if ( Core::settingsManager.email_cfg.use_tls )
8✔
1500
  {
1501
    if ( auto res = curl_easy_setopt( curl, CURLOPT_USE_SSL, CURLUSESSL_ALL ); res != CURLE_OK )
8✔
1502
    {
1503
      return new BError( "curl_easy_setopt(CURLOPT_USE_SSL) failed: " +
×
1504
                         std::string( curl_easy_strerror( res ) ) );
×
1505
    }
1506
  }
1507

1508
  if ( !Core::settingsManager.email_cfg.ca_file.empty() )
8✔
1509
  {
1510
    if ( auto res = curl_easy_setopt( curl, CURLOPT_CAINFO,
8✔
1511
                                      Core::settingsManager.email_cfg.ca_file.c_str() );
1512
         res != CURLE_OK )
1513
    {
1514
      return new BError( "curl_easy_setopt(CURLOPT_CAINFO) failed: " +
×
1515
                         std::string( curl_easy_strerror( res ) ) );
×
1516
    }
1517
  }
1518

1519
  if ( auto res =
8✔
1520
           curl_easy_setopt( curl, CURLOPT_MAIL_FROM, extract_email_address( from ).c_str() );
8✔
1521
       res != CURLE_OK )
1522
  {
1523
    return new BError( "curl_easy_setopt(CURLOPT_MAIL_FROM) failed: " +
×
1524
                       std::string( curl_easy_strerror( res ) ) );
×
1525
  }
1526

1527
  if ( auto* recipient_to_error = handle_recipients( recipient, false ) )  // Handle To recipients
8✔
1528
  {
1529
    return recipient_to_error;
×
1530
  }
1531

1532
  if ( auto* recipient_bcc_error = handle_recipients( bcc, true ) )  // Handle Bcc recipients
8✔
1533
  {
1534
    return recipient_bcc_error;
1✔
1535
  }
1536

1537
  if ( auto res = curl_easy_setopt( curl, CURLOPT_MAIL_RCPT, recipients_slist->get() );
7✔
1538
       res != CURLE_OK )
1539
  {
1540
    return new BError( "curl_easy_setopt(CURLOPT_MAIL_RCPT) failed: " +
×
1541
                       std::string( curl_easy_strerror( res ) ) );
×
1542
  }
1543

1544
  std::string date_header_value = fmt::format(
1545
      "{:%a, %d %b %Y %H:%M:%S %z}",
1546
      std::chrono::time_point_cast<std::chrono::seconds>( std::chrono::system_clock::now() ) );
7✔
1547

1548
  std::vector<std::string> email_headers{ "Date: " + date_header_value,  //
1549
                                          "To: " + to_header_value,      //
1550
                                          "From: " + from->value(),      //
7✔
1551
                                          "Subject: " + subject->value() };
35✔
1552

1553
  for ( const auto& header : email_headers )
35✔
1554
    headers_slist->add( header );
28✔
1555

1556
  if ( auto res = curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers_slist->get() );
7✔
1557
       res != CURLE_OK )
1558
  {
1559
    return new BError( "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " +
×
1560
                       std::string( curl_easy_strerror( res ) ) );
×
1561
  }
1562

1563
  std::shared_ptr<curl_mime> mime( curl_mime_init( curl ), curl_mime_free );
7✔
1564

1565
  curl_mimepart* part = curl_mime_addpart( mime.get() );
7✔
1566

1567
  if ( !part )
7✔
1568
  {
1569
    return new BError( "curl_mime_addpart() failed" );
×
1570
  }
1571

1572
  // `curl_mime_data` copies the string into its internal structure, so using this local is
1573
  // okay. Ref: https://github.com/curl/curl/blob/curl-8_2_1/lib/mime.c#L1380-L1390
1574
  curl_mime_data( part, body->data(), CURL_ZERO_TERMINATED );
7✔
1575

1576
  // `curl_mime_type` copies the string into its internal structure, so using this local is
1577
  // okay. Ref: https://github.com/curl/curl/blob/curl-8_2_1/lib/mime.c#L1461
1578
  curl_mime_type( part, contentType->data() );
7✔
1579

1580
  if ( auto res = curl_easy_setopt( curl, CURLOPT_MIMEPOST, mime.get() ); res != CURLE_OK )
7✔
1581
  {
1582
    return new BError( "curl_easy_setopt(CURLOPT_MIMEPOST) failed: " +
×
1583
                       std::string( curl_easy_strerror( res ) ) );
×
1584
  }
1585

1586
  Core::UOExecutor& this_uoexec = uoexec();
7✔
1587

1588
  if ( !this_uoexec.suspend() )
7✔
1589
  {
1590
    DEBUGLOGLN(
×
1591
        "Script Error in '{}' PC={}: \n"
1592
        "\tThe execution of this script can't be blocked!",
1593
        this_uoexec.scriptname(), this_uoexec.PC );
×
1594
    return new Bscript::BError( "Script can't be blocked" );
×
1595
  }
1596

1597
  weak_ptr<Core::UOExecutor> uoexec_w = this_uoexec.weakptr;
7✔
1598

1599
  Core::networkManager.auxthreadpool->push(
14✔
1600
      [uoexec_w, curl_sp, recipients_slist, headers_slist, mime]()
14✔
1601
      {
1602
        CURL* curl = curl_sp.get();
7✔
1603

1604
        auto res = curl_easy_perform( curl );
7✔
1605

1606
        {
1607
          Core::PolLock lck;
7✔
1608

1609
          if ( !uoexec_w.exists() )
7✔
1610
          {
1611
            DEBUGLOGLN( "SendEmail Script has been destroyed" );
×
1612
            return;
×
1613
          }
1614
          if ( res != CURLE_OK )
7✔
1615
            uoexec_w.get_weakptr()->ValueStack.back().set(
2✔
1616
                new BObject( new BError( curl_easy_strerror( res ) ) ) );
1✔
1617
          else
1618
            uoexec_w.get_weakptr()->ValueStack.back().set( new BObject( new BLong( 1 ) ) );
6✔
1619

1620
          uoexec_w.get_weakptr()->revive();
7✔
1621
        }
7✔
1622
      }
1623

1624
      /* always cleanup */
1625
      // curl_easy_cleanup(), curl_slist_free_all(), curl_mime_free() are
1626
      // performed when the shared pointer deallocates
1627
  );
1628

1629
  return new BLong( 0 );
7✔
1630
}
15✔
1631

1632

1633
size_t OSExecutorModule::sizeEstimate() const
14✔
1634
{
1635
  size_t size = sizeof( *this );
14✔
1636
  for ( const auto& obj : events_ )
14✔
1637
  {
1638
    if ( obj != nullptr )
×
1639
      size += obj->sizeEstimate();
×
1640
  }
1641
  size += Clib::memsize( events_ );
14✔
1642
  return size;
14✔
1643
}
1644
}  // namespace Pol::Module
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc