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