MythTV  master
Programs.cpp
Go to the documentation of this file.
1 /* Programs.cpp
2 
3  Copyright (C) David C. J. Matthews 2004 dm at prolingua.co.uk
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU General Public License
7  as published by the Free Software Foundation; either version 2
8  of the License, or (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18  Or, point your browser to http://www.gnu.org/copyleft/gpl.html
19 
20 */
21 #include <algorithm>
22 #include <limits>
23 
24 #include "libmythbase/mythrandom.h"
25 
26 #include "Programs.h"
27 #include "Ingredients.h"
28 #include "Root.h"
29 #include "BaseClasses.h"
30 #include "ParseNode.h"
31 #include "ASN1Codes.h"
32 #include "Engine.h"
33 #include "Logging.h"
34 #include "freemheg.h"
35 
36 #include <QDateTime>
37 #include <QLocale>
38 #include <QStringList>
39 #include <QUrl>
40 #include <QUrlQuery>
41 
42 /*
43  * Resident programs are subroutines to provide various string and date functions
44  * but also interface to surrounding tuner. They are defined in the UK MHEG profile.
45  * As with many of the more abstruse aspects of MHEG they are not all used in any
46  * applications I've seen so I haven't implemented them.
47  */
48 
50 {
51  MHIngredient::Initialise(p, engine);
52  MHParseNode *pCmdNode = p->GetNamedArg(C_NAME);
53 
54  if (pCmdNode)
55  {
56  pCmdNode->GetArgN(0)->GetStringValue(m_name); // Program name
57  }
58 
59  MHParseNode *pAvail = p->GetNamedArg(C_INITIALLY_AVAILABLE);
60 
61  if (pAvail)
62  {
64  }
65 
66  // The MHEG Standard says that InitiallyAvailable is mandatory and should be false.
67  // That doesn't seem to be the case in some MHEG programs so we force it here.
68  m_fInitiallyActive = false;
69 }
70 
71 void MHProgram::PrintMe(FILE *fd, int nTabs) const
72 {
73  MHIngredient::PrintMe(fd, nTabs);
74  PrintTabs(fd, nTabs);
75  fprintf(fd, ":Name ");
76  m_name.PrintMe(fd, 0);
77  fprintf(fd, "\n");
78 
80  {
81  PrintTabs(fd, nTabs);
82  fprintf(fd, ":InitiallyAvailable false");
83  fprintf(fd, "\n");
84  }
85 }
86 
87 // Set "running" and generate an IsRunning event.
89 {
90  if (m_fRunning)
91  {
92  return;
93  }
94 
96  m_fRunning = true;
97  engine->EventTriggered(this, EventIsRunning);
98 }
99 
100 // This can be called if we change scene while a forked program is running
102 {
103  if (! m_fRunning)
104  {
105  return;
106  }
107 
108  // TODO: Stop the forked program.
110 }
111 
112 void MHResidentProgram::PrintMe(FILE *fd, int nTabs) const
113 {
114  PrintTabs(fd, nTabs);
115  fprintf(fd, "{:ResidentPrg ");
116  MHProgram::PrintMe(fd, nTabs + 1);
117  PrintTabs(fd, nTabs);
118  fprintf(fd, "}\n");
119 }
120 
121 static void SetSuccessFlag(const MHObjectRef &success, bool result, MHEngine *engine)
122 {
123  engine->FindObject(success)->SetVariableValue(result);
124 }
125 
126 // Return an integer value. May throw an exception if it isn't the correct type.
127 static int GetInt(MHParameter *parm, MHEngine *engine)
128 {
129  MHUnion un;
130  un.GetValueFrom(*parm, engine);
132  return un.m_nIntVal;
133 }
134 
135 // Return a bool value. May throw an exception if it isn't the correct type.
136 static bool GetBool(MHParameter *parm, MHEngine *engine)
137 {
138  MHUnion un;
139  un.GetValueFrom(*parm, engine);
141  return un.m_fBoolVal;
142 }
143 
144 // Extract a string value.
145 static void GetString(MHParameter *parm, MHOctetString &str, MHEngine *engine)
146 {
147  MHUnion un;
148  un.GetValueFrom(*parm, engine);
150  str.Copy(un.m_strVal);
151 }
152 
165 static const QDateTime k_mJD_epoch = QDateTime(QDate(1858, 11, 17), QTime(0, 0), Qt::LocalTime);
166 
167 // match types with Qt
168 static inline QDateTime recoverDateTime(int64_t mJDN, int64_t seconds)
169 {
170  QDateTime dt = k_mJD_epoch;
171  return dt.addDays(mJDN).addSecs(seconds);
172 }
173 
174 static void GetCurrentDate(int64_t& mJDN, int& seconds)
175 {
176  auto dt = QDateTime::currentDateTime();
177  mJDN = k_mJD_epoch.daysTo(dt); // returns a qint64
178  seconds = dt.time().msecsSinceStartOfDay() / 1000;
179 }
180 
181 void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, const MHSequence<MHParameter *> &args, MHEngine *engine)
182 {
183  if (! m_fAvailable)
184  {
185  Preparation(engine);
186  }
187 
188  // if (m_fRunning) return; // Strictly speaking there should be only one instance of a program running at a time.
189  Activation(engine);
190  MHLOG(MHLogDetail, QString("Calling program %1").arg(m_name.Printable()));
191 
192  try
193  {
194  // Run the code.
195  if (m_name.Equal("GCD")) // GetCurrentDate - returns local time.
196  {
197  if (args.Size() == 2)
198  {
199  int64_t mJDN = 0;
200  int nTimeAsSecs = 0;
201  GetCurrentDate(mJDN, nTimeAsSecs);
202  int nModJulianDate = std::clamp<int64_t>(mJDN, 0, std::numeric_limits<int>::max());
203 
204  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(nModJulianDate);
205  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(nTimeAsSecs);
206  SetSuccessFlag(success, true, engine);
207  }
208  else
209  {
210  SetSuccessFlag(success, false, engine);
211  }
212  }
213 
214  else if (m_name.Equal("FDa")) // FormatDate
215  {
216  if (args.Size() == 4)
217  {
218  // This is a bit like strftime but not quite.
219  MHOctetString format;
220  GetString(args.GetAt(0), format, engine);
221  int date = GetInt(args.GetAt(1), engine); // As produced in GCD
222  int time = GetInt(args.GetAt(2), engine);
223 
224  QDateTime dt = recoverDateTime(date, time);
225  MHOctetString result;
226 
227  for (int i = 0; i < format.Size(); i++)
228  {
229  char ch = format.GetAt(i);
230  QString buffer {};
231 
232  if (ch == '%')
233  {
234  i++;
235 
236  if (i == format.Size())
237  {
238  break;
239  }
240 
241  ch = format.GetAt(i);
242 
243  switch (ch)
244  {
245  case 'Y': buffer = dt.toString("yyyy"); break;
246  case 'y': buffer = dt.toString("yy"); break;
247  case 'X': buffer = dt.toString("MM"); break;
248  case 'x': buffer = dt.toString("M"); break;
249  case 'D': buffer = dt.toString("dd"); break;
250  case 'd': buffer = dt.toString("d"); break;
251  case 'H': buffer = dt.toString("HH"); break;
252  case 'h': buffer = dt.toString("H"); break;
253  case 'I':
254  // Need AM/PM to get hours as 1-12
255  buffer = dt.toString("HH AP");
256  buffer.chop(3);
257  break;
258  case 'i':
259  buffer = dt.toString("H AP");
260  buffer.chop(3);
261  break;
262  case 'M': buffer = dt.toString("mm"); break;
263  case 'm': buffer = dt.toString("m"); break;
264  case 'S': buffer = dt.toString("ss"); break;
265  case 's': buffer = dt.toString("s"); break;
266  case 'A': buffer = dt.toString("AP"); break;
267  case 'a': buffer = dt.toString("ap"); break;
268  default:
269  buffer= ch;
270  }
271 
272  result.Append(qPrintable(buffer));
273  }
274  else
275  {
276  result.Append(MHOctetString(&ch, 1));
277  }
278  }
279 
280  MHParameter *pResString = args.GetAt(3);
281  engine->FindObject(*(pResString->GetReference()))->SetVariableValue(result);
282  SetSuccessFlag(success, true, engine);
283  }
284  else
285  {
286  SetSuccessFlag(success, false, engine);
287  }
288  }
289 
290  else if (m_name.Equal("GDW")) // GetDayOfWeek - returns the day of week that the date occurred on.
291  {
292  if (args.Size() == 2)
293  {
294  int date = GetInt(args.GetAt(0), engine); // Date as produced in GCD
295  QDateTime dt = recoverDateTime(date, 0);
296  // ETSI ES 202 184 V2.4.1 (2016-06) §11.10.4.4 GetDayOfWeek
297  // specifies "0 represents Sunday, 1 Monday, etc."
298 
299  int dayOfWeek = dt.date().dayOfWeek();
300  // Gregorian calendar, returns 0 if invalid, 1 = Monday to 7 = Sunday
301  if (dayOfWeek == 7)
302  {
303  dayOfWeek = 0;
304  }
305  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(dayOfWeek);
306  SetSuccessFlag(success, true, engine);
307  }
308  else
309  {
310  SetSuccessFlag(success, false, engine);
311  }
312  }
313 
314  else if (m_name.Equal("Rnd")) // Random
315  {
316  if (args.Size() == 2)
317  {
318  int nLimit = GetInt(args.GetAt(0), engine);
319  MHParameter *pResInt = args.GetAt(1);
320  // ETSI ES 202 184 V2.4.1 (2016-06) §11.10.5 Random number function
321  // specifies "The returned value is undefined if the num parameter < 1."
322  // so this is fine.
323  int r = MythRandom(0, nLimit);
324  engine->FindObject(
325  *(pResInt->GetReference()))->SetVariableValue(r);
326  SetSuccessFlag(success, true, engine);
327  }
328  else
329  {
330  SetSuccessFlag(success, false, engine);
331  }
332  }
333 
334  else if (m_name.Equal("CTC")) // CastToContentRef
335  {
336  // Converts a string to a ContentRef.
337  if (args.Size() == 2)
338  {
339  MHOctetString string;
340  GetString(args.GetAt(0), string, engine);
341  MHContentRef result;
342  result.m_contentRef.Copy(string);
343  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(result);
344  SetSuccessFlag(success, true, engine);
345  }
346  else
347  {
348  SetSuccessFlag(success, false, engine);
349  }
350  }
351 
352  else if (m_name.Equal("CTO")) // CastToObjectRef
353  {
354  // Converts a string and an integer to an ObjectRef.
355  if (args.Size() == 3)
356  {
357  MHObjectRef result;
358  GetString(args.GetAt(0), result.m_groupId, engine);
359  result.m_nObjectNo = GetInt(args.GetAt(1), engine);
360  engine->FindObject(*(args.GetAt(2)->GetReference()))->SetVariableValue(result);
361  SetSuccessFlag(success, true, engine);
362  }
363  else
364  {
365  SetSuccessFlag(success, false, engine);
366  }
367  }
368 
369  else if (m_name.Equal("GSL")) // GetStringLength
370  {
371  if (args.Size() == 2)
372  {
373  // Find a substring within a string and return an index to the position.
374  MHOctetString string;
375  GetString(args.GetAt(0), string, engine);
376  MHParameter *pResInt = args.GetAt(1);
377  SetSuccessFlag(success, true, engine);
378  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(string.Size());
379  }
380  else
381  {
382  SetSuccessFlag(success, false, engine);
383  }
384  }
385 
386  else if (m_name.Equal("GSS")) // GetSubString
387  {
388  if (args.Size() == 4) // Extract a sub-string from a string.
389  {
390  MHOctetString string;
391  GetString(args.GetAt(0), string, engine);
392  int nBeginExtract = GetInt(args.GetAt(1), engine);
393  int nEndExtract = GetInt(args.GetAt(2), engine);
394 
395  if (nBeginExtract < 1)
396  {
397  nBeginExtract = 1;
398  }
399 
400  if (nBeginExtract > string.Size())
401  {
402  nBeginExtract = string.Size();
403  }
404 
405  if (nEndExtract < 1)
406  {
407  nEndExtract = 1;
408  }
409 
410  if (nEndExtract > string.Size())
411  {
412  nEndExtract = string.Size();
413  }
414 
415  MHParameter *pResString = args.GetAt(3);
416  // Returns beginExtract to endExtract inclusive.
417  engine->FindObject(*(pResString->GetReference()))->SetVariableValue(
418  MHOctetString(string, nBeginExtract - 1, nEndExtract - nBeginExtract + 1));
419  SetSuccessFlag(success, true, engine);
420  }
421  else
422  {
423  SetSuccessFlag(success, false, engine);
424  }
425  }
426 
427  else if (m_name.Equal("SSS")) // SearchSubString
428  {
429  if (args.Size() == 4)
430  {
431  // Find a substring within a string and return an index to the position.
432  MHOctetString string;
433  MHOctetString searchString;
434  GetString(args.GetAt(0), string, engine);
435  int nStart = GetInt(args.GetAt(1), engine);
436 
437  if (nStart < 1)
438  {
439  nStart = 1;
440  }
441 
442  GetString(args.GetAt(2), searchString, engine);
443  // Strings are indexed from one.
444  int nPos = 0;
445 
446  for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++)
447  {
448  int i = 0;
449 
450  for (i = 0; i < searchString.Size(); i++)
451  {
452  if (searchString.GetAt(i) != string.GetAt(i + nPos))
453  {
454  break;
455  }
456  }
457 
458  if (i == searchString.Size())
459  {
460  break; // Found a match.
461  }
462  }
463 
464  // Set the result.
465  MHParameter *pResInt = args.GetAt(3);
466  SetSuccessFlag(success, true, engine); // Set this first.
467 
468  if (nPos <= string.Size() - searchString.Size()) // Found
469  {
470  // Set the index to the position of the string, counting from 1.
471  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1);
472  }
473  else // Not found. Set the result index to -1
474  {
475  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1);
476  }
477  }
478  else
479  {
480  SetSuccessFlag(success, false, engine);
481  }
482  }
483 
484  else if (m_name.Equal("SES")) // SearchAndExtractSubString
485  {
486  if (args.Size() == 5)
487  {
488  // Find a substring within a string and return an index to the position
489  // and the prefix to the substring.
490  MHOctetString string;
491  MHOctetString searchString;
492  GetString(args.GetAt(0), string, engine);
493  int nStart = GetInt(args.GetAt(1), engine);
494 
495  if (nStart < 1)
496  {
497  nStart = 1;
498  }
499 
500  GetString(args.GetAt(2), searchString, engine);
501  // Strings are indexed from one.
502  int nPos = 0;
503 
504  for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++)
505  {
506  int i = 0;
507 
508  for (i = 0; i < searchString.Size(); i++)
509  {
510  if (searchString.GetAt(i) != string.GetAt(i + nPos))
511  {
512  break; // Doesn't match
513  }
514  }
515 
516  if (i == searchString.Size())
517  {
518  break; // Found a match.
519  }
520  }
521 
522  // Set the results.
523  MHParameter *pResString = args.GetAt(3);
524  MHParameter *pResInt = args.GetAt(4);
525  SetSuccessFlag(success, true, engine); // Set this first.
526 
527  if (nPos <= string.Size() - searchString.Size()) // Found
528  {
529  // Set the index to the position AFTER the string, counting from 1.
530  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1 + searchString.Size());
531  // Return the sequence from nStart-1 of length nPos-nStart+1
532  MHOctetString resultString(string, nStart - 1, nPos - nStart + 1);
533  engine->FindObject(*(pResString->GetReference()))->SetVariableValue(resultString);
534  }
535  else // Not found. Set the result string to empty and the result index to -1
536  {
537  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1);
538  engine->FindObject(*(pResString->GetReference()))->SetVariableValue(MHOctetString(""));
539  }
540  }
541  else
542  {
543  SetSuccessFlag(success, false, engine);
544  }
545  }
546 
547  else if (m_name.Equal("GSI")) // SI_GetServiceIndex
548  {
549  // Returns an index indicating the service
550  if (args.Size() == 2)
551  {
552  MHOctetString string;
553  GetString(args.GetAt(0), string, engine);
554  MHParameter *pResInt = args.GetAt(1);
555  // The format of the service is dvb://netID.[transPortID].serviceID
556  // where the IDs are in hex.
557  // or rec://svc/lcn/N where N is the "logical channel number" i.e. the Freeview channel.
558  QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size());
559  int nResult = engine->GetContext()->GetChannelIndex(str);
560  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nResult);
561  SetSuccessFlag(success, nResult >= 0, engine);
562  }
563  else
564  {
565  SetSuccessFlag(success, false, engine);
566  }
567  }
568 
569  else if (m_name.Equal("TIn")) // SI_TuneIndex - Fork not allowed
570  {
571  // Tunes to an index returned by GSI
572  if (args.Size() == 1)
573  {
574  int nChannel = GetInt(args.GetAt(0), engine);
575  bool res = nChannel >= 0 ? engine->GetContext()->TuneTo(
576  nChannel, engine->GetTuneInfo()) : false;
577  SetSuccessFlag(success, res, engine);
578  }
579  else
580  {
581  SetSuccessFlag(success, false, engine);
582  }
583  }
584  else if (m_name.Equal("TII")) // SI_TuneIndexInfo
585  {
586  // Indicates whether to perform a subsequent TIn quietly or normally.
587  if (args.Size() == 1)
588  {
589  int tuneinfo = GetInt(args.GetAt(0), engine);
590  engine->SetTuneInfo(tuneinfo);
591  SetSuccessFlag(success, true, engine);
592  }
593  else
594  {
595  SetSuccessFlag(success, false, engine);
596  }
597  }
598  else if (m_name.Equal("BSI")) // SI_GetBasicSI
599  {
600  // Returns basic SI information about the service indicated by an index
601  // returned by GSI.
602  // Returns networkID, origNetworkID, transportStreamID, serviceID
603  if (args.Size() == 5)
604  {
605  int channelId = GetInt(args.GetAt(0), engine);
606  int netId = 0;
607  int origNetId = 0;
608  int transportId = 0;
609  int serviceId = 0;
610  // Look the information up in the database.
611  bool res = engine->GetContext()->GetServiceInfo(channelId, netId, origNetId,
612  transportId, serviceId);
613 
614  if (res)
615  {
616  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(netId);
617  engine->FindObject(*(args.GetAt(2)->GetReference()))->SetVariableValue(origNetId);
618  engine->FindObject(*(args.GetAt(3)->GetReference()))->SetVariableValue(transportId);
619  engine->FindObject(*(args.GetAt(4)->GetReference()))->SetVariableValue(serviceId);
620  }
621 
622  SetSuccessFlag(success, res, engine);
623  }
624  else
625  {
626  SetSuccessFlag(success, false, engine);
627  }
628  }
629  else if (m_name.Equal("GBI")) // GetBootInfo
630  {
631  // Gets the NB_info field.
632  MHERROR("GetBootInfo ResidentProgram is not implemented");
633  }
634  else if (m_name.Equal("CCR")) // CheckContentRef
635  {
636  // Sees if an item with a particular content reference is available
637  // in the carousel. This looks like it should block until the file
638  // is available. The profile recommends that this should be forked
639  // rather than called.
640  if (args.Size() == 3)
641  {
642  MHUnion un;
643  un.GetValueFrom(*(args.GetAt(0)), engine);
645  MHContentRef fileName;
646  fileName.Copy(un.m_contentRefVal);
647  QString csPath = engine->GetPathName(fileName.m_contentRef);
648  bool result = false;
649  QByteArray text;
650 
651  // Try to load the object.
652  if (! csPath.isEmpty())
653  {
654  result = engine->GetContext()->GetCarouselData(csPath, text);
655  }
656 
657  // Set the result variable.
658  MHParameter *pResFlag = args.GetAt(1);
659  engine->FindObject(*(pResFlag->GetReference()))->SetVariableValue(result);
660  MHParameter *pResCR = args.GetAt(2);
661  // Copy the file name to the resulting content ref.
662  engine->FindObject(*(pResCR->GetReference()))->SetVariableValue(fileName);
663  SetSuccessFlag(success, true, engine);
664  }
665  else
666  {
667  SetSuccessFlag(success, false, engine);
668  }
669  }
670  else if (m_name.Equal("CGR")) // CheckGroupIDRef
671  {
672  // Sees if an application or scene with a particular group id
673  // is available in the carousel.
674  MHERROR("CheckGroupIDRef ResidentProgram is not implemented");
675  }
676  else if (m_name.Equal("VTG")) // VideoToGraphics
677  {
678  // Video to graphics transformation.
679  MHERROR("VideoToGraphics ResidentProgram is not implemented");
680  }
681  else if (m_name.Equal("SWA")) // SetWidescreenAlignment
682  {
683  // Sets either LetterBox or Centre-cut-out mode.
684  // Seems to be concerned with aligning a 4:3 scene with an underlying 16:9 video
685  MHERROR("SetWidescreenAlignment ResidentProgram is not implemented");
686  }
687  else if (m_name.Equal("GDA")) // GetDisplayAspectRatio
688  {
689  // Returns the aspcet ratio. 4:3 => 1, 16:9 => 2
690  MHERROR("GetDisplayAspectRatio ResidentProgram is not implemented");
691  }
692  else if (m_name.Equal("CIS")) // CI_SendMessage
693  {
694  // Sends a message to a DVB CI application
695  MHERROR("CI_SendMessage ResidentProgram is not implemented");
696  }
697  else if (m_name.Equal("SSM")) // SetSubtitleMode
698  {
699  // Enable or disable subtitles in addition to MHEG.
700  if (args.Size() == 1) {
701  bool status = GetBool(args.GetAt(0), engine);
702  MHLOG(MHLogNotifications, QString("NOTE SetSubtitleMode %1")
703  .arg(status ? "enabled" : "disabled"));
704  // TODO Notify player
705  SetSuccessFlag(success, true, engine);
706  }
707  else SetSuccessFlag(success, false, engine);
708  }
709 
710  else if (m_name.Equal("WAI")) // WhoAmI
711  {
712  // Return a concatenation of the strings we respond to in
713  // GetEngineSupport(UKEngineProfile(X))
714  if (args.Size() == 1)
715  {
716  MHOctetString result;
717 #if 1
718  // BBC Freeview requires a recognized model name which is passed
719  // in a ?whoami=... http query to the interaction channel server.
720  // Otherwise the menu item for iPlayer is not shown
721  result.Copy("SNYPVR");
722 #else
723  result.Copy(engine->MHEGEngineProviderIdString);
724  result.Append(" ");
725  result.Append(engine->GetContext()->GetReceiverId());
726  result.Append(" ");
727  result.Append(engine->GetContext()->GetDSMCCId());
728 #endif
729  MHLOG(MHLogNotifications, "NOTE WhoAmI -> " + QString::fromUtf8((const char *)result.Bytes(), result.Size()) );
730  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(result);
731  SetSuccessFlag(success, true, engine);
732  }
733  else
734  {
735  SetSuccessFlag(success, false, engine);
736  }
737  }
738 
739  // Optional resident programs
740  else if (m_name.Equal("DBG")) // Debug - optional
741  {
742  QString message = "DEBUG: ";
743 
744  for (int i = 0; i < args.Size(); i++)
745  {
746  MHUnion un;
747  un.GetValueFrom(*(args.GetAt(i)), engine);
748 
749  switch (un.m_Type)
750  {
751  case MHUnion::U_Int:
752  message.append(QString("%1").arg(un.m_nIntVal));
753  break;
754  case MHUnion::U_Bool:
755  message.append(un.m_fBoolVal ? "True" : "False");
756  break;
757  case MHUnion::U_String:
758  message.append(QString::fromUtf8((const char *)un.m_strVal.Bytes(), un.m_strVal.Size()));
759  break;
760  case MHUnion::U_ObjRef:
761  message.append(un.m_objRefVal.Printable());
762  break;
764  message.append(un.m_contentRefVal.Printable());
765  break;
766  case MHUnion::U_None:
767  break;
768  }
769  }
770 
771  MHLOG(MHLogNotifications, message);
772  }
773 
774  // NativeApplicationExtension
775  else if (m_name.Equal("SBI")) // SetBroadcastInterrupt
776  {
777  // Required for NativeApplicationExtension
778  // En/dis/able program interruptions e.g. green button
779  if (args.Size() == 1) {
780  bool status = GetBool(args.GetAt(0), engine);
781  MHLOG(MHLogNotifications, QString("NOTE SetBroadcastInterrupt %1")
782  .arg(status ? "enabled" : "disabled"));
783  // Nothing todo at present
784  SetSuccessFlag(success, true, engine);
785  }
786  else SetSuccessFlag(success, false, engine);
787  }
788 
789  // InteractionChannelExtension
790  else if (m_name.Equal("GIS")) { // GetICStatus
791  if (args.Size() == 1)
792  {
793  int ICstatus = engine->GetContext()->GetICStatus();
794  MHLOG(MHLogNotifications, "NOTE InteractionChannel " + QString(
795  ICstatus == 0 ? "active" : ICstatus == 1 ? "inactive" :
796  ICstatus == 2 ? "disabled" : "undefined"));
797  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(ICstatus);
798  SetSuccessFlag(success, true, engine);
799  }
800  else SetSuccessFlag(success, false, engine);
801  }
802  else if (m_name.Equal("RDa")) { // ReturnData
803  if (args.Size() >= 3)
804  {
805  MHOctetString string;
806  GetString(args.GetAt(0), string, engine);
807  QUrl url = QString::fromUtf8((const char *)string.Bytes(), string.Size());
808 
809  // Variable name/value pairs
810  int i = 1;
811  QUrlQuery q;
812  for (; i + 2 < args.Size(); i += 2)
813  {
814  GetString(args.GetAt(i), string, engine);
815  QString name = QString::fromUtf8((const char *)string.Bytes(), string.Size());
816  MHUnion un;
817  un.GetValueFrom(*(args.GetAt(i+1)), engine);
818  q.addQueryItem(name, un.Printable());
819  }
820  url.setQuery(q);
821 
822  MHLOG(MHLogNotifications, QString("NOTE ReturnData %1")
823  .arg(url.toEncoded().constData()) );
824  // NB MHEG-5 says this should be 'post' but 'get; seems to work ok
825  QByteArray text;
826  bool ok = engine->GetContext()->GetCarouselData(url.toEncoded(), text);
827 
828  MHLOG(MHLogNotifications, QString("NOTE ReturnData got %1 bytes")
829  .arg(text.size()) );
830 
831  // HTTP response code, 0= none
832  engine->FindObject(*(args.GetAt(i)->GetReference()))->SetVariableValue(ok ? 200 : 0);
833  // HTTP response data
834  MHOctetString result;
835  result.Append(text.constData());
836  engine->FindObject(*(args.GetAt(i+1)->GetReference()))->SetVariableValue(result);
837 
838  SetSuccessFlag(success, true, engine);
839  }
840  else SetSuccessFlag(success, false, engine);
841  }
842  else if (m_name.Equal("SHF")) { // SetHybridFileSystem
843  if (args.Size() == 2)
844  {
845  MHOctetString string;
846  GetString(args.GetAt(0), string, engine);
847  QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size());
848  GetString(args.GetAt(1), string, engine);
849  QString str2 = QString::fromUtf8((const char *)string.Bytes(), string.Size());
850  // TODO
851  MHLOG(MHLogNotifications, QString("NOTE SetHybridFileSystem %1=%2")
852  .arg(str, str2));
853  SetSuccessFlag(success, false, engine);
854  }
855  else SetSuccessFlag(success, false, engine);
856  }
857  else if (m_name.Equal("PST")) { // PersistentStorageInfo
858  if (args.Size() == 1)
859  {
860  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(true);
861  SetSuccessFlag(success, true, engine);
862  }
863  else SetSuccessFlag(success, false, engine);
864  }
865  else if (m_name.Equal("SCk")) { // SetCookie
866  if (args.Size() == 4)
867  {
868  MHOctetString string;
869  GetString(args.GetAt(0), string, engine);
870  QString id = QString::fromUtf8((const char *)string.Bytes(), string.Size());
871  int iExpiry = GetInt(args.GetAt(1), engine);
872  GetString(args.GetAt(2), string, engine);
873  QString val = QString::fromUtf8((const char *)string.Bytes(), string.Size());
874  bool bSecure = GetBool(args.GetAt(3), engine);
875  // TODO
876  MHLOG(MHLogNotifications, QString("NOTE SetCookie id=%1 MJD=%2 value=%3 secure=%4")
877  .arg(id).arg(iExpiry).arg(val).arg(bSecure) );
878  }
879  else SetSuccessFlag(success, false, engine);
880  }
881  else if (m_name.Equal("GCk")) { // GetCookie
882  MHERROR("GetCookie ResidentProgram is not implemented");
883  }
884 
885  // ICStreamingExtension
886  else if (m_name.Equal("MSP")) // MeasureStreamPerformance
887  {
888  if (args.Size() == 2)
889  {
890  MHOctetString string;
891  GetString(args.GetAt(0), string, engine);
892  QString url = QString::fromUtf8((const char *)string.Bytes(), string.Size());
893  // TODO
894  MHLOG(MHLogNotifications, QString("NOTE MeasureStreamPerformance '%1' %2 bytes")
895  .arg(url).arg(GetInt(args.GetAt(1), engine)) );
896 
897  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(-1);
898  SetSuccessFlag(success, true, engine);
899  }
900  else SetSuccessFlag(success, false, engine);
901  }
902  else if (m_name.Equal("PFG")) { // PromptForGuidance
903  if (args.Size() == 2)
904  {
905  MHOctetString string;
906  GetString(args.GetAt(0), string, engine);
907  QString info = QString::fromUtf8((const char *)string.Bytes(), string.Size());
908  MHLOG(MHLogNotifications, QString("NOTE PromptForGuidance '%1'").arg(info) );
909 
910  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(true);
911  SetSuccessFlag(success, true, engine);
912  }
913  else SetSuccessFlag(success, false, engine);
914 
915  }
916  else if (m_name.Equal("GAP") || // GetAudioDescPref
917  m_name.Equal("GSP")) { // GetSubtitlePref
918  if (args.Size() == 1)
919  {
920  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(false);
921  SetSuccessFlag(success, true, engine);
922  }
923  else SetSuccessFlag(success, false, engine);
924  }
925  else if (m_name.Equal("GPS")) { // GetPINSupport
926  if (args.Size() == 1)
927  {
928  // -1= PIN is not supported
929  // 0= PIN is supported and disabled
930  // 1= PIN is supported and enabled
931  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(0);
932  SetSuccessFlag(success, true, engine);
933  }
934  else SetSuccessFlag(success, false, engine);
935  }
936 
937  // Undocumented functions
938  else if (m_name.Equal("XBM")) {
939  // BBC Freeview passes 1 boolean arg
940  // Required for BBC Freeview iPlayer
941  MHLOG(MHLogNotifications, "NOTE Undocumented ResidentProgram XBM" );
942  if (args.Size() == 1)
943  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(true);
944  SetSuccessFlag(success, true, engine);
945  }
946 
947  else
948  {
949  MHERROR(QString("Unknown ResidentProgram %1").arg(m_name.Printable()));
950  }
951  }
952  catch (...)
953  {
954  QStringList params;
955  for (int i = 0; i < args.Size(); ++i)
956  {
957  MHUnion un;
958  un.GetValueFrom(*(args.GetAt(i)), engine);
959  params += QString(MHUnion::GetAsString(un.m_Type)) + "=" + un.Printable();
960  }
961  MHLOG(MHLogWarning, QString("Arguments (%1)").arg(params.join(",")) );
962 
963  // If something went wrong set the succeeded flag to false
964  SetSuccessFlag(success, false, engine);
965  // And continue on. In particular we need to deactivate.
966  }
967 
968  Deactivation(engine);
969 
970  // At the moment we always treat Fork as Call. If we do get a Fork we should signal that we're done.
971  if (fIsFork)
972  {
973  engine->EventTriggered(this, EventAsyncStopped);
974  }
975 }
976 
978 {
979  MHProgram::Initialise(p, engine);
980  //
981 }
982 
983 void MHRemoteProgram::PrintMe(FILE *fd, int nTabs) const
984 {
985  PrintTabs(fd, nTabs);
986  fprintf(fd, "{:RemotePrg");
987  MHProgram::PrintMe(fd, nTabs + 1);
988  fprintf(fd, "****TODO\n");
989  PrintTabs(fd, nTabs);
990  fprintf(fd, "}\n");
991 }
992 
994 {
995  MHProgram::Initialise(p, engine);
996  //
997 }
998 
999 void MHInterChgProgram::PrintMe(FILE *fd, int nTabs) const
1000 {
1001  PrintTabs(fd, nTabs);
1002  fprintf(fd, "{:InterchgPrg");
1003  MHProgram::PrintMe(fd, nTabs + 1);
1004  fprintf(fd, "****TODO\n");
1005  PrintTabs(fd, nTabs);
1006  fprintf(fd, "}\n");
1007 }
1008 
1009 // Actions.
1011 {
1012  MHElemAction::Initialise(p, engine); // Target
1013  m_succeeded.Initialise(p->GetArgN(1), engine); // Call/fork succeeded flag
1014  // Arguments.
1015  MHParseNode *args = p->GetArgN(2);
1016 
1017  for (int i = 0; i < args->GetSeqCount(); i++)
1018  {
1019  auto *pParm = new MHParameter;
1020  m_parameters.Append(pParm);
1021  pParm->Initialise(args->GetSeqN(i), engine);
1022  }
1023 }
1024 
1025 void MHCall::PrintArgs(FILE *fd, int nTabs) const
1026 {
1027  m_succeeded.PrintMe(fd, nTabs);
1028  fprintf(fd, " ( ");
1029 
1030  for (int i = 0; i < m_parameters.Size(); i++)
1031  {
1032  m_parameters.GetAt(i)->PrintMe(fd, 0);
1033  }
1034 
1035  fprintf(fd, " )");
1036 }
1037 
1039 {
1040  // Output parameters are handled by IndirectRefs so we don't evaluate the parameters here.
1041  Target(engine)->CallProgram(m_fIsFork, m_succeeded, m_parameters, engine);
1042 }
MHContext::GetCarouselData
virtual bool GetCarouselData(const QString &objectPath, QByteArray &result)=0
MHRoot::Activation
virtual void Activation(MHEngine *engine)
Definition: Root.cpp:70
GetString
static void GetString(MHParameter *parm, MHOctetString &str, MHEngine *engine)
Definition: Programs.cpp:145
MHObjectRef
Definition: BaseClasses.h:153
MHUnion::GetAsString
static const char * GetAsString(enum UnionTypes t)
Definition: BaseClasses.cpp:660
build_compdb.args
args
Definition: build_compdb.py:11
MHObjectRef::Initialise
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseClasses.cpp:278
MHInterChgProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:999
MHProgram::m_name
MHOctetString m_name
Definition: Programs.h:50
GetCurrentDate
static void GetCurrentDate(int64_t &mJDN, int &seconds)
Definition: Programs.cpp:174
MHParameter
Definition: BaseClasses.h:258
MHRoot::Deactivation
virtual void Deactivation(MHEngine *engine)
Definition: Root.cpp:86
MHIngredient::Preparation
void Preparation(MHEngine *engine) override
Definition: Ingredients.cpp:159
MHCall::m_parameters
MHOwnPtrSequence< MHParameter > m_parameters
Definition: Programs.h:101
mythrandom.h
MHProgram::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:49
MHRoot::m_fAvailable
bool m_fAvailable
Definition: Root.h:251
MHParameter::GetReference
MHObjectRef * GetReference()
Definition: BaseClasses.cpp:769
MHUnion::CheckType
void CheckType(enum UnionTypes t) const
Definition: BaseClasses.cpp:681
Programs.h
MHParameter::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:734
MHEngine::GetContext
MHContext * GetContext()
Definition: Engine.h:154
ASN1Codes.h
MHEngine
Definition: Engine.h:72
MHEngine::MHEGEngineProviderIdString
static const char * MHEGEngineProviderIdString
Definition: Engine.h:158
MHLogDetail
@ MHLogDetail
Definition: freemheg.h:77
MHObjectRef::m_groupId
MHOctetString m_groupId
Definition: BaseClasses.h:170
MHObjectRef::m_nObjectNo
int m_nObjectNo
Definition: BaseClasses.h:169
MHIngredient::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Ingredients.cpp:49
MHUnion::m_nIntVal
int m_nIntVal
Definition: BaseClasses.h:302
MHOctetString::GetAt
unsigned char GetAt(int i) const
Definition: BaseClasses.h:124
MHOctetString::Bytes
const unsigned char * Bytes() const
Definition: BaseClasses.h:125
C_INITIALLY_AVAILABLE
@ C_INITIALLY_AVAILABLE
Definition: ASN1Codes.h:101
MHResidentProgram::CallProgram
void CallProgram(bool fIsFork, const MHObjectRef &success, const MHSequence< MHParameter * > &args, MHEngine *engine) override
Definition: Programs.cpp:181
recoverDateTime
static QDateTime recoverDateTime(int64_t mJDN, int64_t seconds)
Definition: Programs.cpp:168
MHCall::Perform
void Perform(MHEngine *engine) override
Definition: Programs.cpp:1038
MHUnion::U_ObjRef
@ U_ObjRef
Definition: BaseClasses.h:298
MHUnion::U_Int
@ U_Int
Definition: BaseClasses.h:298
MHRoot::SetVariableValue
virtual void SetVariableValue(const MHUnion &)
Definition: Root.h:108
MHElemAction::Initialise
virtual void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseActions.cpp:31
EventIsRunning
@ EventIsRunning
Definition: Root.h:33
MHSequence< MHParameter * >
MHOctetString
Definition: BaseClasses.h:107
MHEngine::SetTuneInfo
void SetTuneInfo(int tuneinfo)
Definition: Engine.h:165
mythburn.FILE
int FILE
Definition: mythburn.py:139
MHOctetString::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:154
MHContext::TuneTo
virtual bool TuneTo(int channel, int tuneinfo)=0
MHEngine::GetPathName
QString GetPathName(const MHOctetString &str)
Definition: Engine.cpp:523
MHUnion::m_fBoolVal
bool m_fBoolVal
Definition: BaseClasses.h:303
MHCall::PrintArgs
void PrintArgs(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1025
MHCall::m_fIsFork
bool m_fIsFork
Definition: Programs.h:99
MHRoot::m_fRunning
bool m_fRunning
Definition: Root.h:252
C_NAME
@ C_NAME
Definition: ASN1Codes.h:100
MHProgram::m_fInitiallyAvailable
bool m_fInitiallyAvailable
Definition: Programs.h:51
MHEngine::EventTriggered
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:94
MHUnion::U_ContentRef
@ U_ContentRef
Definition: BaseClasses.h:298
MHContext::GetICStatus
virtual int GetICStatus()=0
MHUnion
Definition: BaseClasses.h:283
MHLogNotifications
@ MHLogNotifications
Definition: freemheg.h:73
hardwareprofile.config.p
p
Definition: config.py:33
SetSuccessFlag
static void SetSuccessFlag(const MHObjectRef &success, bool result, MHEngine *engine)
Definition: Programs.cpp:121
ParseNode.h
EventAsyncStopped
@ EventAsyncStopped
Definition: Root.h:34
MHUnion::m_objRefVal
MHObjectRef m_objRefVal
Definition: BaseClasses.h:305
PrintTabs
void PrintTabs(FILE *fd, int n)
Definition: ParseNode.cpp:34
MHParseNode::GetArgN
MHParseNode * GetArgN(int n)
Definition: ParseNode.cpp:78
MHResidentProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:112
MHIngredient::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Ingredients.cpp:107
MHEngine::FindObject
MHRoot * FindObject(const MHObjectRef &oRef, bool failOnNotFound=true)
Definition: Engine.cpp:573
MHRemoteProgram::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:977
MHLOG
#define MHLOG(__level, __text)
Definition: Logging.h:36
MHContentRef::Printable
QString Printable() const
Definition: BaseClasses.h:187
MHUnion::U_None
enum MHUnion::UnionTypes U_None
Engine.h
MHLogWarning
@ MHLogWarning
Definition: freemheg.h:72
Ingredients.h
MHRemoteProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:983
MHUnion::GetValueFrom
void GetValueFrom(const MHParameter &value, MHEngine *engine)
Definition: BaseClasses.cpp:630
MHElemAction::Target
MHRoot * Target(MHEngine *engine)
Definition: BaseActions.cpp:46
MHUnion::m_contentRefVal
MHContentRef m_contentRefVal
Definition: BaseClasses.h:306
MHOctetString::Printable
QString Printable() const
Definition: BaseClasses.h:128
MHCall::m_succeeded
MHObjectRef m_succeeded
Definition: Programs.h:100
MHParseNode::GetBoolValue
bool GetBoolValue()
Definition: ParseNode.cpp:192
MHContext::GetDSMCCId
virtual const char * GetDSMCCId(void)=0
Root.h
MHOctetString::Size
int Size() const
Definition: BaseClasses.h:120
MHContentRef
Definition: BaseClasses.h:174
MHCall::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:1010
MHContentRef::m_contentRef
MHOctetString m_contentRef
Definition: BaseClasses.h:189
MHRoot::CallProgram
virtual void CallProgram(bool, const MHObjectRef &, const MHSequence< MHParameter * > &, MHEngine *)
Definition: Root.h:126
MHProgram::Deactivation
void Deactivation(MHEngine *engine) override
Definition: Programs.cpp:101
BaseClasses.h
MHContext::GetServiceInfo
virtual bool GetServiceInfo(int channelId, int &netId, int &origNetId, int &transportId, int &serviceId)=0
MHEngine::GetTuneInfo
int GetTuneInfo()
Definition: Engine.h:164
MHSequence::Size
int Size() const
Definition: BaseClasses.h:47
MHUnion::Printable
QString Printable() const
Definition: BaseClasses.cpp:690
GetBool
static bool GetBool(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:136
MHInterChgProgram::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:993
MHParseNode
Definition: ParseNode.h:38
GetInt
static int GetInt(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:127
MHUnion::U_String
@ U_String
Definition: BaseClasses.h:298
MHContext::GetChannelIndex
virtual int GetChannelIndex(const QString &str)=0
k_mJD_epoch
static const QDateTime k_mJD_epoch
Midnight on 17 November 1858, the epoch of the modified Julian day.
Definition: Programs.cpp:165
MHSequence::GetAt
BASE GetAt(int i) const
Definition: BaseClasses.h:49
MHProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:71
MHParseNode::GetStringValue
void GetStringValue(MHOctetString &str)
Definition: ParseNode.cpp:203
MHSequence::Append
void Append(BASE b)
Definition: BaseClasses.h:64
freemheg.h
MHOctetString::Equal
bool Equal(const MHOctetString &str) const
Definition: BaseClasses.h:123
MHERROR
#define MHERROR(__text)
Definition: Logging.h:42
Logging.h
MHProgram::Activation
void Activation(MHEngine *engine) override
Definition: Programs.cpp:88
MHIngredient::m_fInitiallyActive
bool m_fInitiallyActive
Definition: Ingredients.h:65
MHUnion::m_strVal
MHOctetString m_strVal
Definition: BaseClasses.h:304
MHContext::GetReceiverId
virtual const char * GetReceiverId(void)=0
MHContentRef::Copy
void Copy(const MHContentRef &cr)
Definition: BaseClasses.h:183
MHOctetString::Append
void Append(const MHOctetString &str)
Definition: BaseClasses.cpp:213
MHObjectRef::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:299
MythRandomStd::MythRandom
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
MHUnion::U_Bool
@ U_Bool
Definition: BaseClasses.h:298
MHOctetString::Copy
void Copy(const MHOctetString &str)
Definition: BaseClasses.cpp:131
MHObjectRef::Printable
QString Printable() const
Definition: BaseClasses.cpp:313