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 
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, searchString;
493  GetString(args.GetAt(0), string, engine);
494  int nStart = GetInt(args.GetAt(1), engine);
495 
496  if (nStart < 1)
497  {
498  nStart = 1;
499  }
500 
501  GetString(args.GetAt(2), searchString, engine);
502  // Strings are indexed from one.
503  int nPos;
504 
505  for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++)
506  {
507  int i;
508 
509  for (i = 0; i < searchString.Size(); i++)
510  {
511  if (searchString.GetAt(i) != string.GetAt(i + nPos))
512  {
513  break;
514  }
515  }
516 
517  if (i == searchString.Size())
518  {
519  break; // Found a match.
520  }
521  }
522 
523  // Set the result.
524  MHParameter *pResInt = args.GetAt(3);
525  SetSuccessFlag(success, true, engine); // Set this first.
526 
527  if (nPos <= string.Size() - searchString.Size()) // Found
528  {
529  // Set the index to the position of the string, counting from 1.
530  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1);
531  }
532  else // Not found. Set the result index to -1
533  {
534  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1);
535  }
536  }
537  else
538  {
539  SetSuccessFlag(success, false, engine);
540  }
541  }
542 
543  else if (m_Name.Equal("SES")) // SearchAndExtractSubString
544  {
545  if (args.Size() == 5)
546  {
547  // Find a substring within a string and return an index to the position
548  // and the prefix to the substring.
549  MHOctetString string, searchString;
550  GetString(args.GetAt(0), string, engine);
551  int nStart = GetInt(args.GetAt(1), engine);
552 
553  if (nStart < 1)
554  {
555  nStart = 1;
556  }
557 
558  GetString(args.GetAt(2), searchString, engine);
559  // Strings are indexed from one.
560  int nPos;
561 
562  for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++)
563  {
564  int i;
565 
566  for (i = 0; i < searchString.Size(); i++)
567  {
568  if (searchString.GetAt(i) != string.GetAt(i + nPos))
569  {
570  break; // Doesn't match
571  }
572  }
573 
574  if (i == searchString.Size())
575  {
576  break; // Found a match.
577  }
578  }
579 
580  // Set the results.
581  MHParameter *pResString = args.GetAt(3);
582  MHParameter *pResInt = args.GetAt(4);
583  SetSuccessFlag(success, true, engine); // Set this first.
584 
585  if (nPos <= string.Size() - searchString.Size()) // Found
586  {
587  // Set the index to the position AFTER the string, counting from 1.
588  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1 + searchString.Size());
589  // Return the sequence from nStart-1 of length nPos-nStart+1
590  MHOctetString resultString(string, nStart - 1, nPos - nStart + 1);
591  engine->FindObject(*(pResString->GetReference()))->SetVariableValue(resultString);
592  }
593  else // Not found. Set the result string to empty and the result index to -1
594  {
595  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1);
596  engine->FindObject(*(pResString->GetReference()))->SetVariableValue(MHOctetString(""));
597  }
598  }
599  else
600  {
601  SetSuccessFlag(success, false, engine);
602  }
603  }
604 
605  else if (m_Name.Equal("GSI")) // SI_GetServiceIndex
606  {
607  // Returns an index indicating the service
608  if (args.Size() == 2)
609  {
610  MHOctetString string;
611  GetString(args.GetAt(0), string, engine);
612  MHParameter *pResInt = args.GetAt(1);
613  // The format of the service is dvb://netID.[transPortID].serviceID
614  // where the IDs are in hex.
615  // or rec://svc/lcn/N where N is the "logical channel number" i.e. the Freeview channel.
616  QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size());
617  int nResult = engine->GetContext()->GetChannelIndex(str);
618  engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nResult);
619  SetSuccessFlag(success, nResult >= 0, engine);
620  }
621  else
622  {
623  SetSuccessFlag(success, false, engine);
624  }
625  }
626 
627  else if (m_Name.Equal("TIn")) // SI_TuneIndex - Fork not allowed
628  {
629  // Tunes to an index returned by GSI
630  if (args.Size() == 1)
631  {
632  int nChannel = GetInt(args.GetAt(0), engine);
633  bool res = nChannel >= 0 ? engine->GetContext()->TuneTo(
634  nChannel, engine->GetTuneInfo()) : false;
635  SetSuccessFlag(success, res, engine);
636  }
637  else
638  {
639  SetSuccessFlag(success, false, engine);
640  }
641  }
642  else if (m_Name.Equal("TII")) // SI_TuneIndexInfo
643  {
644  // Indicates whether to perform a subsequent TIn quietly or normally.
645  if (args.Size() == 1)
646  {
647  int tuneinfo = GetInt(args.GetAt(0), engine);
648  engine->SetTuneInfo(tuneinfo);
649  SetSuccessFlag(success, true, engine);
650  }
651  else
652  {
653  SetSuccessFlag(success, false, engine);
654  }
655  }
656  else if (m_Name.Equal("BSI")) // SI_GetBasicSI
657  {
658  // Returns basic SI information about the service indicated by an index
659  // returned by GSI.
660  // Returns networkID, origNetworkID, transportStreamID, serviceID
661  if (args.Size() == 5)
662  {
663  int channelId = GetInt(args.GetAt(0), engine);
664  int netId, origNetId, transportId, serviceId;
665  // Look the information up in the database.
666  bool res = engine->GetContext()->GetServiceInfo(channelId, netId, origNetId,
667  transportId, serviceId);
668 
669  if (res)
670  {
671  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(netId);
672  engine->FindObject(*(args.GetAt(2)->GetReference()))->SetVariableValue(origNetId);
673  engine->FindObject(*(args.GetAt(3)->GetReference()))->SetVariableValue(transportId);
674  engine->FindObject(*(args.GetAt(4)->GetReference()))->SetVariableValue(serviceId);
675  }
676 
677  SetSuccessFlag(success, res, engine);
678  }
679  else
680  {
681  SetSuccessFlag(success, false, engine);
682  }
683  }
684  else if (m_Name.Equal("GBI")) // GetBootInfo
685  {
686  // Gets the NB_info field.
687  MHERROR("GetBootInfo ResidentProgram is not implemented");
688  }
689  else if (m_Name.Equal("CCR")) // CheckContentRef
690  {
691  // Sees if an item with a particular content reference is available
692  // in the carousel. This looks like it should block until the file
693  // is available. The profile recommends that this should be forked
694  // rather than called.
695  if (args.Size() == 3)
696  {
697  MHUnion un;
698  un.GetValueFrom(*(args.GetAt(0)), engine);
700  MHContentRef fileName;
701  fileName.Copy(un.m_ContentRefVal);
702  QString csPath = engine->GetPathName(fileName.m_ContentRef);
703  bool result = false;
704  QByteArray text;
705 
706  // Try to load the object.
707  if (! csPath.isEmpty())
708  {
709  result = engine->GetContext()->GetCarouselData(csPath, text);
710  }
711 
712  // Set the result variable.
713  MHParameter *pResFlag = args.GetAt(1);
714  engine->FindObject(*(pResFlag->GetReference()))->SetVariableValue(result);
715  MHParameter *pResCR = args.GetAt(2);
716  // Copy the file name to the resulting content ref.
717  engine->FindObject(*(pResCR->GetReference()))->SetVariableValue(fileName);
718  SetSuccessFlag(success, true, engine);
719  }
720  else
721  {
722  SetSuccessFlag(success, false, engine);
723  }
724  }
725  else if (m_Name.Equal("CGR")) // CheckGroupIDRef
726  {
727  // Sees if an application or scene with a particular group id
728  // is available in the carousel.
729  MHERROR("CheckGroupIDRef ResidentProgram is not implemented");
730  }
731  else if (m_Name.Equal("VTG")) // VideoToGraphics
732  {
733  // Video to graphics transformation.
734  MHERROR("VideoToGraphics ResidentProgram is not implemented");
735  }
736  else if (m_Name.Equal("SWA")) // SetWidescreenAlignment
737  {
738  // Sets either LetterBox or Centre-cut-out mode.
739  // Seems to be concerned with aligning a 4:3 scene with an underlying 16:9 video
740  MHERROR("SetWidescreenAlignment ResidentProgram is not implemented");
741  }
742  else if (m_Name.Equal("GDA")) // GetDisplayAspectRatio
743  {
744  // Returns the aspcet ratio. 4:3 => 1, 16:9 => 2
745  MHERROR("GetDisplayAspectRatio ResidentProgram is not implemented");
746  }
747  else if (m_Name.Equal("CIS")) // CI_SendMessage
748  {
749  // Sends a message to a DVB CI application
750  MHERROR("CI_SendMessage ResidentProgram is not implemented");
751  }
752  else if (m_Name.Equal("SSM")) // SetSubtitleMode
753  {
754  // Enable or disable subtitles in addition to MHEG.
755  if (args.Size() == 1) {
756  bool status = GetBool(args.GetAt(0), engine);
757  MHLOG(MHLogNotifications, QString("NOTE SetSubtitleMode %1")
758  .arg(status ? "enabled" : "disabled"));
759  // TODO Notify player
760  SetSuccessFlag(success, true, engine);
761  }
762  else SetSuccessFlag(success, false, engine);
763  }
764 
765  else if (m_Name.Equal("WAI")) // WhoAmI
766  {
767  // Return a concatenation of the strings we respond to in
768  // GetEngineSupport(UKEngineProfile(X))
769  if (args.Size() == 1)
770  {
771  MHOctetString result;
772 #if 1
773  // BBC Freeview requires a recognized model name which is passed
774  // in a ?whoami=... http query to the interaction channel server.
775  // Otherwise the menu item for iPlayer is not shown
776  result.Copy("SNYPVR");
777 #else
778  result.Copy(engine->MHEGEngineProviderIdString);
779  result.Append(" ");
780  result.Append(engine->GetContext()->GetReceiverId());
781  result.Append(" ");
782  result.Append(engine->GetContext()->GetDSMCCId());
783 #endif
784  MHLOG(MHLogNotifications, "NOTE WhoAmI -> " + QString::fromUtf8((const char *)result.Bytes(), result.Size()) );
785  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(result);
786  SetSuccessFlag(success, true, engine);
787  }
788  else
789  {
790  SetSuccessFlag(success, false, engine);
791  }
792  }
793 
794  // Optional resident programs
795  else if (m_Name.Equal("DBG")) // Debug - optional
796  {
797  QString message = "DEBUG: ";
798 
799  for (int i = 0; i < args.Size(); i++)
800  {
801  MHUnion un;
802  un.GetValueFrom(*(args.GetAt(i)), engine);
803 
804  switch (un.m_Type)
805  {
806  case MHUnion::U_Int:
807  message.append(QString("%1").arg(un.m_nIntVal));
808  break;
809  case MHUnion::U_Bool:
810  message.append(un.m_fBoolVal ? "True" : "False");
811  break;
812  case MHUnion::U_String:
813  message.append(QString::fromUtf8((const char *)un.m_StrVal.Bytes(), un.m_StrVal.Size()));
814  break;
815  case MHUnion::U_ObjRef:
816  message.append(un.m_ObjRefVal.Printable());
817  break;
819  message.append(un.m_ContentRefVal.Printable());
820  break;
821  case MHUnion::U_None:
822  break;
823  }
824  }
825 
826  MHLOG(MHLogNotifications, message);
827  }
828 
829  // NativeApplicationExtension
830  else if (m_Name.Equal("SBI")) // SetBroadcastInterrupt
831  {
832  // Required for NativeApplicationExtension
833  // En/dis/able program interruptions e.g. green button
834  if (args.Size() == 1) {
835  bool status = GetBool(args.GetAt(0), engine);
836  MHLOG(MHLogNotifications, QString("NOTE SetBroadcastInterrupt %1")
837  .arg(status ? "enabled" : "disabled"));
838  // Nothing todo at present
839  SetSuccessFlag(success, true, engine);
840  }
841  else SetSuccessFlag(success, false, engine);
842  }
843 
844  // InteractionChannelExtension
845  else if (m_Name.Equal("GIS")) { // GetICStatus
846  if (args.Size() == 1)
847  {
848  int ICstatus = engine->GetContext()->GetICStatus();
849  MHLOG(MHLogNotifications, "NOTE InteractionChannel " + QString(
850  ICstatus == 0 ? "active" : ICstatus == 1 ? "inactive" :
851  ICstatus == 2 ? "disabled" : "undefined"));
852  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(ICstatus);
853  SetSuccessFlag(success, true, engine);
854  }
855  else SetSuccessFlag(success, false, engine);
856  }
857  else if (m_Name.Equal("RDa")) { // ReturnData
858  if (args.Size() >= 3)
859  {
860  MHOctetString string;
861  GetString(args.GetAt(0), string, engine);
862  QUrl url = QString::fromUtf8((const char *)string.Bytes(), string.Size());
863 
864  // Variable name/value pairs
865  int i = 1;
866  QUrlQuery q;
867  for (; i + 2 < args.Size(); i += 2)
868  {
869  GetString(args.GetAt(i), string, engine);
870  QString name = QString::fromUtf8((const char *)string.Bytes(), string.Size());
871  MHUnion un;
872  un.GetValueFrom(*(args.GetAt(i+1)), engine);
873  q.addQueryItem(name, un.Printable());
874  }
875  url.setQuery(q);
876 
877  MHLOG(MHLogNotifications, QString("NOTE ReturnData %1")
878  .arg(url.toEncoded().constData()) );
879  // NB MHEG-5 says this should be 'post' but 'get; seems to work ok
880  QByteArray text;
881  bool ok = engine->GetContext()->GetCarouselData(url.toEncoded(), text);
882 
883  MHLOG(MHLogNotifications, QString("NOTE ReturnData got %1 bytes")
884  .arg(text.size()) );
885 
886  // HTTP response code, 0= none
887  engine->FindObject(*(args.GetAt(i)->GetReference()))->SetVariableValue(ok ? 200 : 0);
888  // HTTP response data
889  MHOctetString result;
890  result.Append(text.constData());
891  engine->FindObject(*(args.GetAt(i+1)->GetReference()))->SetVariableValue(result);
892 
893  SetSuccessFlag(success, true, engine);
894  }
895  else SetSuccessFlag(success, false, engine);
896  }
897  else if (m_Name.Equal("SHF")) { // SetHybridFileSystem
898  if (args.Size() == 2)
899  {
900  MHOctetString string;
901  GetString(args.GetAt(0), string, engine);
902  QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size());
903  GetString(args.GetAt(1), string, engine);
904  QString str2 = QString::fromUtf8((const char *)string.Bytes(), string.Size());
905  // TODO
906  MHLOG(MHLogNotifications, QString("NOTE SetHybridFileSystem %1=%2")
907  .arg(str).arg(str2));
908  SetSuccessFlag(success, false, engine);
909  }
910  else SetSuccessFlag(success, false, engine);
911  }
912  else if (m_Name.Equal("PST")) { // PersistentStorageInfo
913  if (args.Size() == 1)
914  {
915  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(true);
916  SetSuccessFlag(success, true, engine);
917  }
918  else SetSuccessFlag(success, false, engine);
919  }
920  else if (m_Name.Equal("SCk")) { // SetCookie
921  if (args.Size() == 4)
922  {
923  MHOctetString string;
924  GetString(args.GetAt(0), string, engine);
925  QString id = QString::fromUtf8((const char *)string.Bytes(), string.Size());
926  int iExpiry = GetInt(args.GetAt(1), engine);
927  GetString(args.GetAt(2), string, engine);
928  QString val = QString::fromUtf8((const char *)string.Bytes(), string.Size());
929  bool bSecure = GetBool(args.GetAt(3), engine);
930  // TODO
931  MHLOG(MHLogNotifications, QString("NOTE SetCookie id=%1 MJD=%2 value=%3 secure=%4")
932  .arg(id).arg(iExpiry).arg(val).arg(bSecure) );
933  }
934  else SetSuccessFlag(success, false, engine);
935  }
936  else if (m_Name.Equal("GCk")) { // GetCookie
937  MHERROR("GetCookie ResidentProgram is not implemented");
938  }
939 
940  // ICStreamingExtension
941  else if (m_Name.Equal("MSP")) // MeasureStreamPerformance
942  {
943  if (args.Size() == 2)
944  {
945  MHOctetString string;
946  GetString(args.GetAt(0), string, engine);
947  QString url = QString::fromUtf8((const char *)string.Bytes(), string.Size());
948  // TODO
949  MHLOG(MHLogNotifications, QString("NOTE MeasureStreamPerformance '%1' %2 bytes")
950  .arg(url).arg(GetInt(args.GetAt(1), engine)) );
951 
952  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(-1);
953  SetSuccessFlag(success, true, engine);
954  }
955  else SetSuccessFlag(success, false, engine);
956  }
957  else if (m_Name.Equal("PFG")) { // PromptForGuidance
958  if (args.Size() == 2)
959  {
960  MHOctetString string;
961  GetString(args.GetAt(0), string, engine);
962  QString info = QString::fromUtf8((const char *)string.Bytes(), string.Size());
963  MHLOG(MHLogNotifications, QString("NOTE PromptForGuidance '%1'").arg(info) );
964 
965  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(true);
966  SetSuccessFlag(success, true, engine);
967  }
968  else SetSuccessFlag(success, false, engine);
969 
970  }
971  else if (m_Name.Equal("GAP")) { // GetAudioDescPref
972  if (args.Size() == 1)
973  {
974  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(false);
975  SetSuccessFlag(success, true, engine);
976  }
977  else SetSuccessFlag(success, false, engine);
978  }
979  else if (m_Name.Equal("GSP")) { // GetSubtitlePref
980  if (args.Size() == 1)
981  {
982  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(false);
983  SetSuccessFlag(success, true, engine);
984  }
985  else SetSuccessFlag(success, false, engine);
986  }
987  else if (m_Name.Equal("GPS")) { // GetPINSupport
988  if (args.Size() == 1)
989  {
990  // -1= PIN is not supported
991  // 0= PIN is supported and disabled
992  // 1= PIN is supported and enabled
993  engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(0);
994  SetSuccessFlag(success, true, engine);
995  }
996  else SetSuccessFlag(success, false, engine);
997  }
998 
999  // Undocumented functions
1000  else if (m_Name.Equal("XBM")) {
1001  // BBC Freeview passes 1 boolean arg
1002  // Required for BBC Freeview iPlayer
1003  MHLOG(MHLogNotifications, "NOTE Undocumented ResidentProgram XBM" );
1004  if (args.Size() == 1)
1005  engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(true);
1006  SetSuccessFlag(success, true, engine);
1007  }
1008 
1009  else
1010  {
1011  MHERROR(QString("Unknown ResidentProgram %1").arg(m_Name.Printable()));
1012  }
1013  }
1014  catch (char const *)
1015  {
1016  QStringList params;
1017  for (int i = 0; i < args.Size(); ++i)
1018  {
1019  MHUnion un;
1020  un.GetValueFrom(*(args.GetAt(i)), engine);
1021  params += QString(MHUnion::GetAsString(un.m_Type)) + "=" + un.Printable();
1022  }
1023  MHLOG(MHLogWarning, QString("Arguments (%1)").arg(params.join(",")) );
1024 
1025  // If something went wrong set the succeeded flag to false
1026  SetSuccessFlag(success, false, engine);
1027  // And continue on. In particular we need to deactivate.
1028  }
1029 
1030  Deactivation(engine);
1031 
1032  // At the moment we always treat Fork as Call. If we do get a Fork we should signal that we're done.
1033  if (fIsFork)
1034  {
1035  engine->EventTriggered(this, EventAsyncStopped);
1036  }
1037 }
1038 
1040 {
1041  MHProgram::Initialise(p, engine);
1042  //
1043 }
1044 
1045 void MHRemoteProgram::PrintMe(FILE *fd, int nTabs) const
1046 {
1047  PrintTabs(fd, nTabs);
1048  fprintf(fd, "{:RemotePrg");
1049  MHProgram::PrintMe(fd, nTabs + 1);
1050  fprintf(fd, "****TODO\n");
1051  PrintTabs(fd, nTabs);
1052  fprintf(fd, "}\n");
1053 }
1054 
1056 {
1057  MHProgram::Initialise(p, engine);
1058  //
1059 }
1060 
1061 void MHInterChgProgram::PrintMe(FILE *fd, int nTabs) const
1062 {
1063  PrintTabs(fd, nTabs);
1064  fprintf(fd, "{:InterchgPrg");
1065  MHProgram::PrintMe(fd, nTabs + 1);
1066  fprintf(fd, "****TODO\n");
1067  PrintTabs(fd, nTabs);
1068  fprintf(fd, "}\n");
1069 }
1070 
1071 // Actions.
1073 {
1074  MHElemAction::Initialise(p, engine); // Target
1075  m_Succeeded.Initialise(p->GetArgN(1), engine); // Call/fork succeeded flag
1076  // Arguments.
1077  MHParseNode *args = p->GetArgN(2);
1078 
1079  for (int i = 0; i < args->GetSeqCount(); i++)
1080  {
1081  MHParameter *pParm = new MHParameter;
1082  m_Parameters.Append(pParm);
1083  pParm->Initialise(args->GetSeqN(i), engine);
1084  }
1085 }
1086 
1087 void MHCall::PrintArgs(FILE *fd, int nTabs) const
1088 {
1089  m_Succeeded.PrintMe(fd, nTabs);
1090  fprintf(fd, " ( ");
1091 
1092  for (int i = 0; i < m_Parameters.Size(); i++)
1093  {
1094  m_Parameters.GetAt(i)->PrintMe(fd, 0);
1095  }
1096 
1097  fprintf(fd, " )");
1098 }
1099 
1101 {
1102  // Output parameters are handled by IndirectRefs so we don't evaluate the parameters here.
1103  Target(engine)->CallProgram(m_fIsFork, m_Succeeded, m_Parameters, engine);
1104 }
void Initialise(MHParseNode *p, MHEngine *engine)
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:1039
bool m_fBoolVal
Definition: BaseClasses.h:293
void Preparation(MHEngine *engine) override
void Append(const MHOctetString &str)
int m_nIntVal
Definition: BaseClasses.h:292
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:1045
bool m_fIsFork
Definition: Programs.h:99
virtual void SetVariableValue(const MHUnion &)
Definition: Root.h:101
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:1087
void CallProgram(bool fIsFork, const MHObjectRef &success, const MHSequence< MHParameter * > &args, MHEngine *engine) override
Definition: Programs.cpp:158
bool m_fAvailable
Definition: Root.h:207
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:122
unsigned char GetAt(int i) const
Definition: BaseClasses.h:121
bool m_fInitiallyActive
Definition: Ingredients.h:65
MHContentRef m_ContentRefVal
Definition: BaseClasses.h:296
void GetValueFrom(const MHParameter &value, MHEngine *engine)
unsigned char r
Definition: ParseText.cpp:329
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:180
#define C_NAME
Definition: ASN1Codes.h:97
void CheckType(enum UnionTypes) const
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:117
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:184
QString Printable() const
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:1072
QString Printable() const
Definition: BaseClasses.h:125
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:93
bool Equal(const MHOctetString &str) const
Definition: BaseClasses.h:120
virtual int GetChannelIndex(const QString &str)=0
void GetStringValue(MHOctetString &str)
Definition: ParseNode.cpp:203
MHParseNode * GetNamedArg(int nTag)
Definition: ParseNode.cpp:110
void PrintMe(FILE *fd, int nTabs) const
virtual bool GetCarouselData(QString objectPath, QByteArray &result)=0
virtual int GetICStatus()=0
const char * name
Definition: ParseText.cpp:328
int GetTuneInfo()
Definition: Engine.h:163
void PrintMe(FILE *fd, int nTabs) const
bool m_fRunning
Definition: Root.h:208
MHObjectRef m_Succeeded
Definition: Programs.h:100
int FILE
Definition: mythburn.py:110
#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:113
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:575
MHOctetString m_StrVal
Definition: BaseClasses.h:294
QString GetPathName(const MHOctetString &str)
Definition: Engine.cpp:525
#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:186
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:1100
void Initialise(MHParseNode *p, MHEngine *engine)
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:54
MHOctetString m_GroupId
Definition: BaseClasses.h:167
MHObjectRef m_ObjRefVal
Definition: BaseClasses.h:295
MHParseNode * GetArgN(int n)
Definition: ParseNode.cpp:78
static long int random(void)
Definition: compat.h:149
void Append(BASE b)
Definition: BaseClasses.h:62
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1061
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:1055
static bool GetBool(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:141
virtual const char * GetReceiverId(void)=0