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