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