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