]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
VDR 1.2.6 compatibility
[vompserver.git] / mvpclient.c
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "mvpclient.h"
22
23 // This is here else it causes compile errors with something in libdvbmpeg
24 #include <vdr/menu.h>
25
26 MVPClient::MVPClient(char* tconfigDirExtra, int tsocket)
27  : tcp(tsocket)
28 {
29   lp = NULL;
30   rp = NULL;
31   recordingManager = NULL;
32   log = Log::getInstance();
33   loggedIn = false;
34   configDirExtra = tconfigDirExtra;
35 }
36
37 MVPClient::~MVPClient()
38 {
39   log->log("Client", Log::DEBUG, "MVP client destructor");
40   if (lp)
41   {
42     delete lp;
43     lp = NULL;
44   }
45   else if (rp)
46   {
47     writeResumeData();
48
49     delete rp;
50     delete recordingManager;
51     rp = NULL;
52     recordingManager = NULL;
53   }
54
55   if (loggedIn) cleanConfig();
56 }
57
58 ULLONG MVPClient::ntohll(ULLONG a)
59 {
60   return htonll(a);
61 }
62
63 ULLONG MVPClient::htonll(ULLONG a)
64 {
65   #if BYTE_ORDER == BIG_ENDIAN
66     return a;
67   #else
68     ULLONG b = 0;
69
70     b = ((a << 56) & 0xFF00000000000000ULL)
71       | ((a << 40) & 0x00FF000000000000ULL)
72       | ((a << 24) & 0x0000FF0000000000ULL)
73       | ((a <<  8) & 0x000000FF00000000ULL)
74       | ((a >>  8) & 0x00000000FF000000ULL)
75       | ((a >> 24) & 0x0000000000FF0000ULL)
76       | ((a >> 40) & 0x000000000000FF00ULL)
77       | ((a >> 56) & 0x00000000000000FFULL) ;
78
79     return b;
80   #endif
81 }
82
83 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
84 {
85   cChannel* channel = NULL;
86
87   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
88   {
89     if (!channel->GroupSep())
90     {
91       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
92
93       if (channel->Number() == (int)channelNumber)
94       {
95         int vpid = channel->Vpid();
96 #if VDRVERSNUM < 10300
97         int apid1 = channel->Apid1();
98 #else
99         int apid1 = channel->Apid(0);
100 #endif
101         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
102         return channel;
103       }
104     }
105   }
106
107   if (!channel)
108   {
109     log->log("Client", Log::DEBUG, "Channel not found");
110   }
111
112   return channel;
113 }
114
115 void MVPClient::writeResumeData()
116 {
117   config.setValueLong("ResumeData",
118                           (char*)rp->getCurrentRecording()->FileName(),
119                           rp->frameNumberFromPosition(rp->getLastPosition()) );
120 }
121
122 void MVPClient::sendULONG(ULONG ul)
123 {
124   UCHAR sendBuffer[8];
125   *(ULONG*)&sendBuffer[0] = htonl(4);
126   *(ULONG*)&sendBuffer[4] = htonl(ul);
127
128   tcp.sendPacket(sendBuffer, 8);
129   log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
130 }
131
132 void MVPClientStartThread(void* arg)
133 {
134   MVPClient* m = (MVPClient*)arg;
135   m->run2();
136   // Nothing external to this class has a reference to it
137   // This is the end of the thread.. so delete m
138   delete m;
139   pthread_exit(NULL);
140 }
141
142 int MVPClient::run()
143 {
144   if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
145   log->log("Client", Log::DEBUG, "MVPClient run success");
146   return 1;
147 }
148
149 void MVPClient::run2()
150 {
151   // Thread stuff
152   sigset_t sigset;
153   sigfillset(&sigset);
154   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
155   pthread_detach(runThread);  // Detach
156
157   tcp.disableReadTimeout();
158
159   tcp.setSoKeepTime(3);
160   tcp.setNonBlocking();
161
162   UCHAR* buffer;
163   UCHAR* data;
164   int packetLength;
165   ULONG opcode;
166   int result = 0;
167
168   while(1)
169   {
170     log->log("Client", Log::DEBUG, "Waiting");
171     buffer = (UCHAR*)tcp.receivePacket();
172     log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
173     if (buffer == NULL)
174     {
175       log->log("Client", Log::DEBUG, "Detected connection closed");
176       break;
177     }
178
179     packetLength = tcp.getDataLength() - 4;
180     opcode = ntohl(*(ULONG*)buffer);
181     data = buffer + 4;
182
183     if (!loggedIn && (opcode != 1))
184     {
185       free(buffer);
186       break;
187     }
188
189     log->log("Client", Log::DEBUG, "SwitchOp");
190     switch(opcode)
191     {
192       case 1:
193         result = processLogin(data, packetLength);
194         break;
195       case 2:
196         result = processGetRecordingsList(data, packetLength);
197         break;
198       case 3:
199         result = processDeleteRecording(data, packetLength);
200         break;
201       case 5:
202         result = processGetChannelsList(data, packetLength);
203         break;
204       case 6:
205         result = processStartStreamingChannel(data, packetLength);
206         break;
207       case 7:
208         result = processGetBlock(data, packetLength);
209         break;
210       case 8:
211         result = processStopStreaming(data, packetLength);
212         break;
213       case 9:
214         result = processStartStreamingRecording(data, packetLength);
215         break;
216       case 10:
217         result = processGetChannelSchedule(data, packetLength);
218         break;
219       case 11:
220         result = processConfigSave(data, packetLength);
221         break;
222       case 12:
223         result = processConfigLoad(data, packetLength);
224         break;
225       case 13:
226         result = processReScanRecording(data, packetLength);         // FIXME obselete
227         break;
228       case 14:
229         result = processGetTimers(data, packetLength);
230         break;
231       case 15:
232         result = processSetTimer(data, packetLength);
233         break;
234       case 16:
235         result = processPositionFromFrameNumber(data, packetLength);
236         break;
237       case 17:
238         result = processFrameNumberFromPosition(data, packetLength);
239         break;
240       case 18:
241         result = processMoveRecording(data, packetLength);
242         break;
243       case 19:
244         result = processGetIFrame(data, packetLength);
245         break;
246       case 20:
247         result = processGetRecInfo(data, packetLength);
248         break;
249       case 21:
250         result = processGetMarks(data, packetLength);
251         break;
252       case 22:
253         result = processGetChannelPids(data, packetLength);
254         break;
255     }
256
257     free(buffer);
258     if (!result) break;
259   }
260 }
261
262 int MVPClient::processLogin(UCHAR* buffer, int length)
263 {
264   if (length != 6) return 0;
265
266   // Open the config
267
268   const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
269   if (!configDir)
270   {
271     log->log("Client", Log::DEBUG, "No config dir!");
272     return 0;
273   }
274
275   char configFileName[PATH_MAX];
276   snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", configDir, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
277   config.init(configFileName);
278
279   // Send the login reply
280
281   time_t timeNow = time(NULL);
282   struct tm* timeStruct = localtime(&timeNow);
283   int timeOffset = timeStruct->tm_gmtoff;
284
285   UCHAR sendBuffer[12];
286   *(ULONG*)&sendBuffer[0] = htonl(8);
287   *(ULONG*)&sendBuffer[4] = htonl(timeNow);
288   *(signed int*)&sendBuffer[8] = htonl(timeOffset);
289
290   tcp.sendPacket(sendBuffer, 12);
291   log->log("Client", Log::DEBUG, "written login reply");
292
293   loggedIn = true;
294   return 1;
295 }
296
297 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
298 {
299   UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
300   int count = 4; // leave space for the packet length
301   char* point;
302
303
304   int FreeMB;
305   int Percent = VideoDiskSpace(&FreeMB);
306   int Total = (FreeMB / (100 - Percent)) * 100;
307
308   *(ULONG*)&sendBuffer[count] = htonl(Total);
309   count += sizeof(ULONG);
310   *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
311   count += sizeof(ULONG);
312   *(ULONG*)&sendBuffer[count] = htonl(Percent);
313   count += sizeof(ULONG);
314
315
316   cRecordings Recordings;
317   Recordings.Load();
318
319   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
320   {
321     if (count > 49000) break; // just how big is that hard disk?!
322     *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
323     count += 4;
324
325     point = (char*)recording->Name();
326     strcpy((char*)&sendBuffer[count], point);
327     count += strlen(point) + 1;
328
329     point = (char*)recording->FileName();
330     strcpy((char*)&sendBuffer[count], point);
331     count += strlen(point) + 1;
332   }
333
334   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
335
336   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
337
338   tcp.sendPacket(sendBuffer, count);
339   delete[] sendBuffer;
340   log->log("Client", Log::DEBUG, "Written list");
341
342   return 1;
343 }
344
345 int MVPClient::processDeleteRecording(UCHAR* data, int length)
346 {
347   // data is a pointer to the fileName string
348
349   cRecordings Recordings;
350   Recordings.Load(); // probably have to do this
351
352   cRecording* recording = Recordings.GetByName((char*)data);
353
354   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
355
356   if (recording)
357   {
358     log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
359
360     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
361     if (!rc)
362     {
363       if (recording->Delete())
364       {
365         // Copy svdrp's way of doing this, see if it works
366 #if VDRVERSNUM > 10300
367         ::Recordings.DelByName(recording->FileName());
368 #endif
369         sendULONG(1);
370       }
371       else
372       {
373         sendULONG(2);
374       }
375     }
376     else
377     {
378       sendULONG(3);
379     }
380   }
381   else
382   {
383     sendULONG(4);
384   }
385
386   return 1;
387 }
388
389 int MVPClient::processMoveRecording(UCHAR* data, int length)
390 {
391   log->log("Client", Log::DEBUG, "Process move recording");
392   char* fileName = (char*)data;
393   char* newPath = NULL;
394
395   for (int k = 0; k < length; k++)
396   {
397     if (data[k] == '\0')
398     {
399       newPath = (char*)&data[k+1];
400       break;
401     }
402   }
403   if (!newPath) return 0;
404
405   cRecordings Recordings;
406   Recordings.Load(); // probably have to do this
407
408   cRecording* recording = Recordings.GetByName((char*)fileName);
409
410   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
411
412   if (recording)
413   {
414     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
415     if (!rc)
416     {
417       log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
418       log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
419       log->log("Client", Log::DEBUG, "to: %s", newPath);
420
421       const char* t = recording->FileName();
422
423       char* dateDirName = NULL;   int k;
424       char* titleDirName = NULL;  int j;
425
426       // Find the datedirname
427       for(k = strlen(t) - 1; k >= 0; k--)
428       {
429         if (t[k] == '/')
430         {
431           log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
432           dateDirName = new char[strlen(&t[k+1]) + 1];
433           strcpy(dateDirName, &t[k+1]);
434           break;
435         }
436       }
437
438       // Find the titledirname
439
440       for(j = k-1; j >= 0; j--)
441       {
442         if (t[j] == '/')
443         {
444           log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
445           titleDirName = new char[(k - j - 1) + 1];
446           memcpy(titleDirName, &t[j+1], k - j - 1);
447           titleDirName[k - j - 1] = '\0';
448           break;
449         }
450       }
451
452       log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
453       log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
454
455       log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
456
457       char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
458       log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
459       sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
460
461       // FIXME Check whether this already exists before mkdiring it
462
463       log->log("Client", Log::DEBUG, "%s", newContainer);
464
465
466       struct stat dstat;
467       int statret = stat(newContainer, &dstat);
468       if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
469       {
470         log->log("Client", Log::DEBUG, "new dir does not exist");
471         int mkdirret = mkdir(newContainer, 0755);
472         if (mkdirret != 0)
473         {
474           delete[] dateDirName;
475           delete[] titleDirName;
476           delete[] newContainer;
477           sendULONG(5);
478           return 1;
479         }
480       }
481       else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
482       {
483         delete[] dateDirName;
484         delete[] titleDirName;
485         delete[] newContainer;
486         sendULONG(5);
487         return 1;
488       }
489
490       // Ok, the directory container has been made, or it pre-existed.
491
492       char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
493       sprintf(newDir, "%s/%s", newContainer, dateDirName);
494
495       log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
496       int renameret = rename(t, newDir);
497       if (renameret == 0)
498       {
499         // Success. Test for remove old dir containter
500         char* oldTitleDir = new char[k+1];
501         memcpy(oldTitleDir, t, k);
502         oldTitleDir[k] = '\0';
503         log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
504         rmdir(oldTitleDir); // can't do anything about a fail result at this point.
505         delete[] oldTitleDir;
506       }
507
508       if (renameret == 0)
509       {
510 #if VDRVERSNUM > 10311
511         // Tell VDR
512         ::Recordings.Update();
513 #endif
514         // Success. Send a different packet from just a ulong
515         int totalLength = 4 + 4 + strlen(newDir) + 1;
516         UCHAR* sendBuffer = new UCHAR[totalLength];
517         *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
518         *(ULONG*)&sendBuffer[4] = htonl(1); // success
519         strcpy((char*)&sendBuffer[8], newDir);
520         tcp.sendPacket(sendBuffer, totalLength);
521         delete[] sendBuffer;
522       }
523       else
524       {
525         sendULONG(5);
526       }
527
528       delete[] dateDirName;
529       delete[] titleDirName;
530       delete[] newContainer;
531       delete[] newDir;
532     }
533     else
534     {
535       sendULONG(3);
536     }
537   }
538   else
539   {
540     sendULONG(4);
541   }
542
543   return 1;
544 }
545
546 int MVPClient::processGetChannelsList(UCHAR* data, int length)
547 {
548   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
549   int count = 4; // leave space for the packet length
550   char* point;
551   ULONG type;
552
553   char* chanConfig = config.getValueString("General", "Channels");
554   int allChans = 1;
555   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
556
557   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
558   {
559 #if VDRVERSNUM < 10300
560     if (!channel->GroupSep() && (!channel->Ca() || allChans))
561 #else
562     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
563 #endif
564     {
565       log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
566
567       if (channel->Vpid()) type = 1;
568 #if VDRVERSNUM < 10300
569       else type = 2;
570 #else
571       else if (channel->Apid(0)) type = 2;
572       else continue;
573 #endif
574
575       if (count > 49000) break;
576       *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
577       count += 4;
578
579       *(ULONG*)&sendBuffer[count] = htonl(type);
580       count += 4;
581
582       point = (char*)channel->Name();
583       strcpy((char*)&sendBuffer[count], point);
584       count += strlen(point) + 1;
585     }
586   }
587
588   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
589
590   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
591
592   tcp.sendPacket(sendBuffer, count);
593   delete[] sendBuffer;
594   log->log("Client", Log::DEBUG, "Written channels list");
595
596   return 1;
597 }
598
599 int MVPClient::processGetChannelPids(UCHAR* data, int length)
600 {
601   ULONG channelNumber = ntohl(*(ULONG*)data);
602
603   cChannel* channel = channelFromNumber(channelNumber);
604   if (!channel)
605   {
606     sendULONG(0);
607     return 1;
608   }
609
610   ULONG numApids = 0;
611   ULONG spaceRequired = 12; // 4 for length field, 4 for vpid, 4 for number of apids
612   // Work out space required and number of Apids
613
614 #if VDRVERSNUM < 10300
615
616   log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
617   log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
618
619   if (channel->Apid2())
620   {
621     numApids = 2;
622     spaceRequired += 10; // 8 + 2 nulls
623   }
624   else if (channel->Apid1())
625   {
626     numApids = 1;
627     spaceRequired += 5; // 4 + 1 null
628   }
629   else
630   {
631     numApids = 0;
632   }
633
634 #else
635
636   for (const int *Apid = channel->Apids(); *Apid; Apid++)
637   {
638     spaceRequired += 4 + strlen(channel->Alang(numApids)) + 1; // 4 for pid, length of string + \0
639     numApids++;
640   }
641 #endif
642
643
644   // Format of response
645   // vpid
646   // number of apids
647   // {
648   //    apid
649   //    lang string
650   // }
651
652   UCHAR* sendBuffer = new UCHAR[spaceRequired];
653   ULONG point = 0;
654   *(ULONG*)&sendBuffer[point] = htonl(spaceRequired - 4);   point += 4;   // take off first 4 bytes
655   *(ULONG*)&sendBuffer[point] = htonl(channel->Vpid());     point += 4;
656   *(ULONG*)&sendBuffer[point] = htonl(numApids);            point += 4;
657
658 #if VDRVERSNUM < 10300
659   if (numApids >= 1)
660   {
661     *(ULONG*)&sendBuffer[point] = htonl(channel->Apid1());  point += 4;
662     sendBuffer[point] = '\0';                               point += 1;
663   }
664   if (numApids == 2)
665   {
666     *(ULONG*)&sendBuffer[point] = htonl(channel->Apid2());  point += 4;
667     sendBuffer[point] = '\0';                               point += 1;
668   }
669 #else
670   for (ULONG i = 0; i < numApids; i++)
671   {
672     *(ULONG*)&sendBuffer[point] = htonl(channel->Apid(i));  point += 4;
673     strcpy((char*)&sendBuffer[point], channel->Alang(i));   point += strlen(channel->Alang(i)) + 1;
674   }
675 #endif
676
677 //  printf("About to send getchannelpids response. length = %u\n", spaceRequired);
678   tcp.dump(sendBuffer, spaceRequired);
679
680   tcp.sendPacket(sendBuffer, spaceRequired);
681   delete[] sendBuffer;
682   log->log("Client", Log::DEBUG, "Written channels pids");
683
684   return 1;
685 }
686
687 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
688 {
689   log->log("Client", Log::DEBUG, "length = %i", length);
690   ULONG channelNumber = ntohl(*(ULONG*)data);
691
692   cChannel* channel = channelFromNumber(channelNumber);
693   if (!channel)
694   {
695     sendULONG(0);
696     return 1;
697   }
698
699   // get the priority we should use
700   int fail = 1;
701   int priority = config.getValueLong("General", "Live priority", &fail);
702   if (!fail)
703   {
704     log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
705   }
706   else
707   {
708     log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
709     priority = 0;
710   }
711
712   // a bit of sanity..
713   if (priority < 0) priority = 0;
714   if (priority > 99) priority = 99;
715
716   log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
717   lp = MVPReceiver::create(channel, priority);
718
719   if (!lp)
720   {
721     sendULONG(0);
722     return 1;
723   }
724
725   if (!lp->init())
726   {
727     delete lp;
728     lp = NULL;
729     sendULONG(0);
730     return 1;
731   }
732
733   sendULONG(1);
734   return 1;
735 }
736
737 int MVPClient::processStopStreaming(UCHAR* data, int length)
738 {
739   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
740   if (lp)
741   {
742     delete lp;
743     lp = NULL;
744   }
745   else if (rp)
746   {
747     writeResumeData();
748
749     delete rp;
750     delete recordingManager;
751     rp = NULL;
752     recordingManager = NULL;
753   }
754
755   sendULONG(1);
756   return 1;
757 }
758
759 int MVPClient::processGetBlock(UCHAR* data, int length)
760 {
761   if (!lp && !rp)
762   {
763     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
764     return 0;
765   }
766
767   ULLONG position = ntohll(*(ULLONG*)data);
768   data += sizeof(ULLONG);
769   ULONG amount = ntohl(*(ULONG*)data);
770
771   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
772
773   UCHAR sendBuffer[amount + 4];
774   ULONG amountReceived = 0; // compiler moan.
775   if (lp)
776   {
777     log->log("Client", Log::DEBUG, "getting from live");
778     amountReceived = lp->getBlock(&sendBuffer[4], amount);
779
780     if (!amountReceived)
781     {
782       // vdr has possibly disconnected the receiver
783       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
784       delete lp;
785       lp = NULL;
786     }
787   }
788   else if (rp)
789   {
790     log->log("Client", Log::DEBUG, "getting from recording");
791     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
792   }
793
794   if (!amountReceived)
795   {
796     sendULONG(0);
797     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
798   }
799   else
800   {
801     *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
802     tcp.sendPacket(sendBuffer, amountReceived + 4);
803     log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
804   }
805
806   return 1;
807 }
808
809 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
810 {
811   // data is a pointer to the fileName string
812
813   recordingManager = new cRecordings;
814   recordingManager->Load();
815
816   cRecording* recording = recordingManager->GetByName((char*)data);
817
818   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
819
820   if (recording)
821   {
822     rp = new RecPlayer(recording);
823
824     UCHAR sendBuffer[16];
825     *(ULONG*)&sendBuffer[0] = htonl(12);
826     *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
827     *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
828
829     tcp.sendPacket(sendBuffer, 16);
830     log->log("Client", Log::DEBUG, "written totalLength");
831   }
832   else
833   {
834     delete recordingManager;
835     recordingManager = NULL;
836   }
837   return 1;
838 }
839
840 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
841 {
842   ULLONG retval = 0;
843
844   ULONG frameNumber = ntohl(*(ULONG*)data);
845   data += 4;
846
847   if (!rp)
848   {
849     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
850   }
851   else
852   {
853     retval = rp->positionFromFrameNumber(frameNumber);
854   }
855
856   UCHAR sendBuffer[12];
857   *(ULONG*)&sendBuffer[0] = htonl(8);
858   *(ULLONG*)&sendBuffer[4] = htonll(retval);
859
860   tcp.sendPacket(sendBuffer, 12);
861   log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
862   return 1;
863 }
864
865 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
866 {
867   ULONG retval = 0;
868
869   ULLONG position = ntohll(*(ULLONG*)data);
870   data += 8;
871
872   if (!rp)
873   {
874     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
875   }
876   else
877   {
878     retval = rp->frameNumberFromPosition(position);
879   }
880
881   UCHAR sendBuffer[8];
882   *(ULONG*)&sendBuffer[0] = htonl(4);
883   *(ULONG*)&sendBuffer[4] = htonl(retval);
884
885   tcp.sendPacket(sendBuffer, 8);
886   log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
887   return 1;
888 }
889
890 int MVPClient::processGetIFrame(UCHAR* data, int length)
891 {
892   bool success = false;
893
894   ULONG frameNumber = ntohl(*(ULONG*)data);
895   data += 4;
896   ULONG direction = ntohl(*(ULONG*)data);
897   data += 4;
898
899   ULLONG rfilePosition = 0;
900   ULONG rframeNumber = 0;
901   ULONG rframeLength = 0;
902
903   if (!rp)
904   {
905     log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
906   }
907   else
908   {
909     success = rp->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
910   }
911
912   // returns file position, frame number, length
913
914   UCHAR sendBuffer[20];
915   int packetLength;
916
917   if (success)
918   {
919     packetLength = 20;
920     *(ULONG*)&sendBuffer[0] = htonl(16);
921     *(ULLONG*)&sendBuffer[4] = htonll(rfilePosition);
922     *(ULONG*)&sendBuffer[12] = htonl(rframeNumber);
923     *(ULONG*)&sendBuffer[16] = htonl(rframeLength);
924   }
925   else
926   {
927     packetLength = 8;
928     *(ULONG*)&sendBuffer[0] = htonl(4);
929     *(ULONG*)&sendBuffer[4] = 0;
930   }
931
932   log->log("Client", Log::DEBUG, "%llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
933
934   tcp.sendPacket(sendBuffer, packetLength);
935   log->log("Client", Log::DEBUG, "Wrote GNIF reply to client");
936   return 1;
937 }
938
939 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
940 {
941   ULONG channelNumber = ntohl(*(ULONG*)data);
942   data += 4;
943   ULONG startTime = ntohl(*(ULONG*)data);
944   data += 4;
945   ULONG duration = ntohl(*(ULONG*)data);
946
947   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
948
949   cChannel* channel = channelFromNumber(channelNumber);
950   if (!channel)
951   {
952     sendULONG(0);
953     log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
954     return 1;
955   }
956
957   log->log("Client", Log::DEBUG, "Got channel");
958
959 #if VDRVERSNUM < 10300
960   cMutexLock MutexLock;
961   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
962 #else
963   cSchedulesLock MutexLock;
964   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
965 #endif
966   if (!Schedules)
967   {
968     sendULONG(0);
969     log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
970     return 1;
971   }
972
973   log->log("Client", Log::DEBUG, "Got schedule!s! object");
974
975   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
976   if (!Schedule)
977   {
978     sendULONG(0);
979     log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
980     return 1;
981   }
982
983   log->log("Client", Log::DEBUG, "Got schedule object");
984
985   UCHAR* sendBuffer = (UCHAR*)malloc(100000);
986   ULONG sendBufferLength = 100000;
987   ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
988
989   char* empty = "";
990
991   // assign all the event info to temp vars then we know exactly what size they are
992   ULONG thisEventID;
993   ULONG thisEventTime;
994   ULONG thisEventDuration;
995   const char* thisEventTitle;
996   const char* thisEventSubTitle;
997   const char* thisEventDescription;
998
999   ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
1000   ULONG thisEventLength;
1001
1002 #if VDRVERSNUM < 10300
1003
1004   const cEventInfo *event;
1005   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1006   {
1007     event = Schedule->GetEventNumber(eventNumber);
1008
1009     thisEventID = event->GetEventID();
1010     thisEventTime = event->GetTime();
1011     thisEventDuration = event->GetDuration();
1012     thisEventTitle = event->GetTitle();
1013     thisEventSubTitle = event->GetSubtitle();
1014     thisEventDescription = event->GetExtendedDescription();
1015
1016 #else
1017
1018   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1019   {
1020     thisEventID = event->EventID();
1021     thisEventTime = event->StartTime();
1022     thisEventDuration = event->Duration();
1023     thisEventTitle = event->Title();
1024     thisEventSubTitle = NULL;
1025     thisEventDescription = event->Description();
1026
1027 #endif
1028
1029     log->log("Client", Log::DEBUG, "Got an event object %p", event);
1030
1031     //in the past filter
1032     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1033
1034     //start time filter
1035     if ((thisEventTime + thisEventDuration) <= startTime) continue;
1036
1037     //duration filter
1038     if (thisEventTime >= (startTime + duration)) continue;
1039
1040     if (!thisEventTitle) thisEventTitle = empty;
1041     if (!thisEventSubTitle) thisEventSubTitle = empty;
1042     if (!thisEventDescription) thisEventDescription = empty;
1043
1044     thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
1045
1046     log->log("Client", Log::DEBUG, "Done s1");
1047
1048     // now extend the buffer if necessary
1049     if ((sendBufferUsed + thisEventLength) > sendBufferLength)
1050     {
1051       log->log("Client", Log::DEBUG, "Extending buffer");
1052       sendBufferLength += 100000;
1053       UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
1054       if (temp == NULL)
1055       {
1056         free(sendBuffer);
1057         UCHAR sendBuffer2[8];
1058         *(ULONG*)&sendBuffer2[0] = htonl(4);
1059         *(ULONG*)&sendBuffer2[4] = htonl(0);
1060         tcp.sendPacket(sendBuffer2, 8);
1061         log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
1062         return 1;
1063       }
1064       sendBuffer = temp;
1065     }
1066
1067     log->log("Client", Log::DEBUG, "Done s2");
1068
1069     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID);       sendBufferUsed += sizeof(ULONG);
1070     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime);     sendBufferUsed += sizeof(ULONG);
1071     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
1072
1073     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle);       sendBufferUsed += strlen(thisEventTitle) + 1;
1074     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle);    sendBufferUsed += strlen(thisEventSubTitle) + 1;
1075     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
1076
1077     log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
1078   }
1079
1080   log->log("Client", Log::DEBUG, "Got all event data");
1081
1082   if (sendBufferUsed == sizeof(ULONG))
1083   {
1084     // No data
1085     sendULONG(0);
1086     log->log("Client", Log::DEBUG, "Written 0 because no data");
1087   }
1088   else
1089   {
1090     // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
1091     *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
1092     tcp.sendPacket(sendBuffer, sendBufferUsed);
1093     log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
1094   }
1095
1096   free(sendBuffer);
1097
1098   return 1;
1099 }
1100
1101 int MVPClient::processConfigSave(UCHAR* buffer, int length)
1102 {
1103   char* section = (char*)buffer;
1104   char* key = NULL;
1105   char* value = NULL;
1106
1107   for (int k = 0; k < length; k++)
1108   {
1109     if (buffer[k] == '\0')
1110     {
1111       if (!key)
1112       {
1113         key = (char*)&buffer[k+1];
1114       }
1115       else
1116       {
1117         value = (char*)&buffer[k+1];
1118         break;
1119       }
1120     }
1121   }
1122
1123   // if the last string (value) doesnt have null terminator, give up
1124   if (buffer[length - 1] != '\0') return 0;
1125
1126   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1127   if (config.setValueString(section, key, value))
1128   {
1129     sendULONG(1);
1130   }
1131   else
1132   {
1133     sendULONG(0);
1134   }
1135
1136   return 1;
1137 }
1138
1139 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
1140 {
1141   char* section = (char*)buffer;
1142   char* key = NULL;
1143
1144   for (int k = 0; k < length; k++)
1145   {
1146     if (buffer[k] == '\0')
1147     {
1148       key = (char*)&buffer[k+1];
1149       break;
1150     }
1151   }
1152
1153   char* value = config.getValueString(section, key);
1154
1155   if (value)
1156   {
1157     UCHAR sendBuffer[4 + strlen(value) + 1];
1158     *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
1159     strcpy((char*)&sendBuffer[4], value);
1160     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
1161
1162     log->log("Client", Log::DEBUG, "Written config load packet");
1163     delete[] value;
1164   }
1165   else
1166   {
1167     UCHAR sendBuffer[8];
1168     *(ULONG*)&sendBuffer[0] = htonl(4);
1169     *(ULONG*)&sendBuffer[4] = htonl(0);
1170     tcp.sendPacket(sendBuffer, 8);
1171
1172     log->log("Client", Log::DEBUG, "Written config load failed packet");
1173   }
1174
1175   return 1;
1176 }
1177
1178 void MVPClient::cleanConfig()
1179 {
1180   log->log("Client", Log::DEBUG, "Clean config");
1181
1182   cRecordings Recordings;
1183   Recordings.Load();
1184
1185   int numReturns;
1186   int length;
1187   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
1188   char* position = resumes;
1189   for(int k = 0; k < numReturns; k++)
1190   {
1191     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
1192
1193     cRecording* recording = Recordings.GetByName(position);
1194     if (!recording)
1195     {
1196       // doesn't exist anymore
1197       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
1198       config.deleteValue("ResumeData", position);
1199     }
1200     else
1201     {
1202       log->log("Client", Log::DEBUG, "This recording still exists");
1203     }
1204
1205     position += strlen(position) + 1;
1206   }
1207
1208   delete[] resumes;
1209 }
1210
1211
1212
1213
1214
1215
1216 /*
1217     event = Schedule->GetPresentEvent();
1218
1219     fprintf(f, "\n\nCurrent event\n\n");
1220
1221     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1222     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1223     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1224     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1225
1226     event = Schedule->GetFollowingEvent();
1227
1228     fprintf(f, "\n\nFollowing event\n\n");
1229
1230     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1231     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1232     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1233     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1234
1235     fprintf(f, "\n\n");
1236 */
1237
1238 /*
1239     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1240     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1241     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1242     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1243
1244     fprintf(f, "\n\n");
1245 */
1246
1247 /*
1248
1249
1250 void MVPClient::test2()
1251 {
1252   FILE* f = fopen("/tmp/s.txt", "w");
1253
1254 #if VDRVERSNUM < 10300
1255   cMutexLock MutexLock;
1256   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1257 #else
1258   cSchedulesLock MutexLock;
1259   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1260 #endif
1261
1262   if (!Schedules)
1263   {
1264     fprintf(f, "Schedules = NULL\n");
1265     fclose(f);
1266     return;
1267   }
1268
1269   fprintf(f, "Schedules dump:\n");
1270   Schedules->Dump(f);
1271
1272
1273   const cSchedule *Schedule;
1274   int scheduleNumber = 0;
1275
1276   tChannelID tchid;
1277   cChannel *thisChannel;
1278
1279 #if VDRVERSNUM < 10300
1280   const cEventInfo *event;
1281   int eventNumber = 0;
1282 #else
1283   const cEvent *event;
1284 #endif
1285
1286 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
1287 //    Schedule = Schedules->GetSchedule();
1288   Schedule = Schedules->First();
1289   if (!Schedule)
1290   {
1291     fprintf(f, "First Schedule = NULL\n");
1292     fclose(f);
1293     return;
1294   }
1295
1296   while (Schedule)
1297   {
1298     fprintf(f, "Schedule #%i\n", scheduleNumber);
1299     fprintf(f, "-------------\n\n");
1300
1301 #if VDRVERSNUM < 10300
1302     tchid = Schedule->GetChannelID();
1303 #else
1304     tchid = Schedule->ChannelID();
1305 #endif
1306
1307 #if VDRVERSNUM < 10300
1308     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1309     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1310 #else
1311 //  put the count at the end.
1312 #endif
1313
1314     thisChannel = Channels.GetByChannelID(tchid, true);
1315     if (thisChannel)
1316     {
1317       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1318     }
1319     else
1320     {
1321       fprintf(f, "thisChannel = NULL for tchid\n");
1322     }
1323
1324 #if VDRVERSNUM < 10300
1325     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1326     {
1327       event = Schedule->GetEventNumber(eventNumber);
1328       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1329       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1330       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1331       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1332       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1333       fprintf(f, "Event %u dump:\n", eventNumber);
1334       event->Dump(f);
1335       fprintf(f, "\n\n");
1336     }
1337 #else
1338 //  This whole section needs rewriting to walk the list.
1339     event = Schedule->Events()->First();
1340     while (event) {
1341       event = Schedule->Events()->Next(event);
1342     }
1343 #endif
1344
1345
1346     fprintf(f, "\nDump from object:\n");
1347     Schedule->Dump(f);
1348     fprintf(f, "\nEND\n");
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358     fprintf(f, "End of current Schedule\n\n\n");
1359
1360     Schedule = (const cSchedule *)Schedules->Next(Schedule);
1361     scheduleNumber++;
1362   }
1363
1364   fclose(f);
1365 }
1366
1367
1368
1369 */
1370
1371
1372
1373 /*
1374   const cEventInfo *GetPresentEvent(void) const;
1375   const cEventInfo *GetFollowingEvent(void) const;
1376   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1377   const cEventInfo *GetEventAround(time_t tTime) const;
1378   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1379
1380
1381   const unsigned char GetTableID(void) const;
1382   const char *GetTimeString(void) const;
1383   const char *GetEndTimeString(void) const;
1384   const char *GetDate(void) const;
1385   bool IsFollowing(void) const;
1386   bool IsPresent(void) const;
1387   const char *GetExtendedDescription(void) const;
1388   const char *GetSubtitle(void) const;
1389   const char *GetTitle(void) const;
1390   unsigned short GetEventID(void) const;
1391   long GetDuration(void) const;
1392   time_t GetTime(void) const;
1393   tChannelID GetChannelID(void) const;
1394   int GetChannelNumber(void) const { return nChannelNumber; }
1395   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1396   void Dump(FILE *f, const char *Prefix = "") const;
1397
1398 */
1399
1400
1401 /*
1402 void MVPClient::test(int channelNumber)
1403 {
1404   FILE* f = fopen("/tmp/test.txt", "w");
1405
1406   cMutexLock MutexLock;
1407   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1408
1409   if (!Schedules)
1410   {
1411     fprintf(f, "Schedules = NULL\n");
1412     fclose(f);
1413     return;
1414   }
1415
1416   fprintf(f, "Schedules dump:\n");
1417 //  Schedules->Dump(f);
1418
1419   const cSchedule *Schedule;
1420   cChannel *thisChannel;
1421   const cEventInfo *event;
1422
1423   thisChannel = channelFromNumber(channelNumber);
1424   if (!thisChannel)
1425   {
1426     fprintf(f, "thisChannel = NULL\n");
1427     fclose(f);
1428     return;
1429   }
1430
1431   Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1432 //    Schedule = Schedules->GetSchedule();
1433 //  Schedule = Schedules->First();
1434   if (!Schedule)
1435   {
1436     fprintf(f, "First Schedule = NULL\n");
1437     fclose(f);
1438     return;
1439   }
1440
1441   fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1442
1443   // For some channels VDR seems to pick a random point in time to
1444   // start dishing out events, but they are in order
1445   // at some point in the list the time snaps to the current event
1446
1447
1448
1449
1450   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1451   {
1452     event = Schedule->GetEventNumber(eventNumber);
1453     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1454     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1455     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1456     fprintf(f, "\n\n");
1457   }
1458
1459   fprintf(f, "\nEND\n");
1460
1461   fclose(f);
1462 }
1463
1464 */
1465
1466
1467
1468 /*
1469
1470
1471 Right, so
1472
1473 Schedules = the collection of all the Schedule objects
1474 Schedule  = One schedule, contants all the events for a channel
1475 Event     = One programme
1476
1477
1478 Want:
1479
1480 Event ID
1481 Time
1482 Duration
1483 Title
1484 Subtitle (used for "Programmes resume at ...")
1485 Description
1486
1487 IsPresent ? easy to work out tho. Oh it doesn't always work
1488
1489 */
1490
1491 /*
1492 void MVPClient::test2()
1493 {
1494   log->log("-", Log::DEBUG, "Timers List");
1495
1496   for (int i = 0; i < Timers.Count(); i++)
1497   {
1498     cTimer *timer = Timers.Get(i);
1499     //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1500     log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1501 #if VDRVERSNUM < 10300
1502     log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->Active(), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime());
1503 #else
1504     log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->HasFlags(tfActive), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime());
1505 #endif
1506     log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1507     log->log("-", Log::DEBUG, "");
1508   }
1509
1510   // asprintf(&buffer, "%d:%s:%s  :%04d:%04d:%d:%d:%s:%s\n",
1511 //            active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1512 //            PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1513 }
1514 */
1515
1516 /*
1517 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1518 recording is a bool, 0 for not currently recording, 1 for currently recording
1519 pending is a bool, 0 for would not be trying to record this right now, 1 for would/is trying to record this right now
1520 */
1521
1522
1523 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1524 {
1525   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1526   int count = 4; // leave space for the packet length
1527
1528   const char* fileName;
1529   cTimer *timer;
1530   int numTimers = Timers.Count();
1531
1532   *(ULONG*)&sendBuffer[count] = htonl(numTimers);    count += 4;
1533
1534   for (int i = 0; i < numTimers; i++)
1535   {
1536     if (count > 49000) break;
1537
1538     timer = Timers.Get(i);
1539
1540 #if VDRVERSNUM < 10300
1541     *(ULONG*)&sendBuffer[count] = htonl(timer->Active());                 count += 4;
1542 #else
1543     *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive));       count += 4;
1544 #endif
1545     *(ULONG*)&sendBuffer[count] = htonl(timer->Recording());              count += 4;
1546     *(ULONG*)&sendBuffer[count] = htonl(timer->Pending());                count += 4;
1547     *(ULONG*)&sendBuffer[count] = htonl(timer->Priority());               count += 4;
1548     *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime());               count += 4;
1549     *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number());      count += 4;
1550     *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime());              count += 4;
1551     *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime());               count += 4;
1552
1553     fileName = timer->File();
1554     strcpy((char*)&sendBuffer[count], fileName);
1555     count += strlen(fileName) + 1;
1556   }
1557
1558   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
1559
1560   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1561
1562   tcp.sendPacket(sendBuffer, count);
1563   delete[] sendBuffer;
1564   log->log("Client", Log::DEBUG, "Written timers list");
1565
1566   return 1;
1567 }
1568
1569 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1570 {
1571   char* timerString = new char[strlen((char*)buffer) + 1];
1572   strcpy(timerString, (char*)buffer);
1573
1574 #if VDRVERSNUM < 10300
1575
1576   // If this is VDR 1.2 the date part of the timer string must be reduced
1577   // to just DD rather than YYYY-MM-DD
1578
1579   int s = 0; // source
1580   int d = 0; // destination
1581   int c = 0; // count
1582   while(c != 2) // copy up to date section, including the second ':'
1583   {
1584     timerString[d] = buffer[s];
1585     if (buffer[s] == ':') c++;
1586     ++s;
1587     ++d;
1588   }
1589   // now it has copied up to the date section
1590   c = 0;
1591   while(c != 2) // waste YYYY-MM-
1592   {
1593     if (buffer[s] == '-') c++;
1594     ++s;
1595   }
1596   // now source is at the DD
1597   memcpy(&timerString[d], &buffer[s], length - s);
1598   d += length - s;
1599   timerString[d] = '\0';
1600
1601   log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1602   log->log("Client", Log::DEBUG, "%s", timerString);
1603
1604 #endif
1605
1606   cTimer *timer = new cTimer;
1607   if (timer->Parse((char*)timerString))
1608   {
1609     cTimer *t = Timers.GetTimer(timer);
1610     if (!t)
1611     {
1612       Timers.Add(timer);
1613 #if VDRVERSNUM < 10300
1614       Timers.Save();
1615 #else
1616       Timers.SetModified();
1617 #endif
1618       sendULONG(0);
1619       return 1;
1620     }
1621     else
1622     {
1623       sendULONG(1);
1624     }
1625   }
1626   else
1627   {
1628      sendULONG(2);
1629   }
1630   delete timer;
1631   return 1;
1632 }
1633
1634 int MVPClient::processGetRecInfo(UCHAR* data, int length)
1635 {
1636   // data is a pointer to the fileName string
1637
1638   cRecordings Recordings;
1639   Recordings.Load(); // probably have to do this
1640
1641   cRecording *recording = Recordings.GetByName((char*)data);
1642
1643   time_t timerStart = 0;
1644   time_t timerStop = 0;
1645   char* summary = NULL;
1646   ULONG resumePoint = 0;
1647
1648   if (!recording)
1649   {
1650     log->log("Client", Log::ERR, "GetRecInfo found no recording");
1651     sendULONG(0);
1652     return 1;
1653   }
1654
1655   ULONG sendBufferSize = 10000;
1656   UCHAR* sendBuffer = (UCHAR*)malloc(sendBufferSize);
1657   ULONG pos = 4; // leave first 4 bytes for size field
1658
1659
1660   /* Return packet:
1661   4 bytes: start time for timer
1662   4 bytes: end time for timer
1663   4 bytes: resume point
1664   string: summary
1665   4 bytes: num components
1666   {
1667     1 byte: stream
1668     1 byte: type
1669     string: language
1670     string: description
1671   }
1672
1673   */
1674
1675   // Get current timer
1676
1677   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1678   if (rc)
1679   {
1680     timerStart = rc->Timer()->StartTime();
1681     timerStop = rc->Timer()->StopTime();
1682     log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1683   }
1684
1685   *(time_t*)&sendBuffer[pos] = htonl(timerStart);    pos += 4;
1686   *(time_t*)&sendBuffer[pos] = htonl(timerStop);     pos += 4;
1687
1688   // Get resume point
1689
1690   char* value = config.getValueString("ResumeData", (char*)data);
1691   if (value)
1692   {
1693     resumePoint = strtoul(value, NULL, 10);
1694     delete[] value;
1695   }
1696   log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1697
1698   *(ULONG*)&sendBuffer[pos] = htonl(resumePoint);    pos += 4;
1699
1700
1701   // Get summary
1702
1703 #if VDRVERSNUM < 10300
1704   summary = (char*)recording->Summary();
1705 #else
1706   const cRecordingInfo *Info = recording->Info();
1707   summary = (char*)Info->ShortText();
1708   if (isempty(summary)) summary = (char*)Info->Description();
1709 #endif
1710   log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1711   if (summary)
1712   {
1713     // memory insanity...
1714     if ((sendBufferSize - pos) < (strlen(summary) + 500)) // random
1715     {
1716       UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + strlen(summary) + 10000);
1717       if (newBuffer)
1718       {
1719         sendBuffer = newBuffer;
1720         sendBufferSize += strlen(summary) + 10000;
1721       }
1722       else
1723       {
1724         free(sendBuffer);
1725         sendULONG(0);
1726         return 1;
1727       }
1728     }
1729
1730     strcpy((char*)&sendBuffer[pos], summary);
1731     pos += strlen(summary) + 1;
1732   }
1733   else
1734   {
1735     strcpy((char*)&sendBuffer[pos], "");
1736     pos += 1;
1737   }
1738
1739
1740   // Get channels
1741
1742 #if VDRVERSNUM < 10300
1743
1744   // Send 0 for numchannels - this signals the client this info is not available
1745   *(ULONG*)&sendBuffer[pos] = 0;    pos += 4;
1746
1747 #else
1748   const cComponents* components = Info->Components();
1749
1750   log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1751
1752   if (!components)
1753   {
1754     *(ULONG*)&sendBuffer[pos] = htonl(0);    pos += 4;
1755   }
1756   else
1757   {
1758     *(ULONG*)&sendBuffer[pos] = htonl(components->NumComponents());    pos += 4;
1759
1760     tComponent* component;
1761     for (int i = 0; i < components->NumComponents(); i++)
1762     {
1763       component = components->Component(i);
1764
1765       // memory insanity...
1766       ULONG extraNeeded = 2 + (component->language ? strlen(component->language) : 0)
1767                             + (component->description ? strlen(component->description) : 0) + 2;
1768
1769       if ((sendBufferSize - pos) < extraNeeded)
1770       {
1771         UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + extraNeeded + 10000);
1772         if (newBuffer)
1773         {
1774           sendBuffer = newBuffer;
1775           sendBufferSize += extraNeeded + 10000;
1776         }
1777         else
1778         {
1779           free(sendBuffer);
1780           sendULONG(0);
1781           return 1;
1782         }
1783       }
1784
1785       log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1786       sendBuffer[pos] = component->stream;  pos += 1;
1787       sendBuffer[pos] = component->type;    pos += 1;
1788       if (component->language)
1789       {
1790         strcpy((char*)&sendBuffer[pos], component->language);
1791         pos += strlen(component->language) + 1;
1792       }
1793       else
1794       {
1795         strcpy((char*)&sendBuffer[pos], "");
1796         pos += 1;
1797       }
1798       if (component->description)
1799       {
1800         strcpy((char*)&sendBuffer[pos], component->description);
1801         pos += strlen(component->description) + 1;
1802       }
1803       else
1804       {
1805         strcpy((char*)&sendBuffer[pos], "");
1806         pos += 1;
1807       }
1808
1809     }
1810   }
1811
1812 #endif
1813
1814   // Done. send it
1815
1816   *(ULONG*)&sendBuffer[0] = htonl(pos - 4); // -4 :  take off the size field
1817
1818   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1819
1820   tcp.sendPacket(sendBuffer, pos);
1821   delete[] sendBuffer;
1822   log->log("Client", Log::DEBUG, "Written getrecinfo");
1823
1824   return 1;
1825 }
1826
1827
1828
1829
1830 // FIXME obselete
1831
1832 int MVPClient::processReScanRecording(UCHAR* data, int length)
1833 {
1834   if (!rp)
1835   {
1836     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1837     return 0;
1838   }
1839
1840   rp->scan();
1841
1842   UCHAR sendBuffer[16];
1843   *(ULONG*)&sendBuffer[0] = htonl(12);
1844   *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
1845   *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
1846
1847   tcp.sendPacket(sendBuffer, 16);
1848   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1849   return 1;
1850 }
1851
1852 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1853
1854
1855 int MVPClient::processGetMarks(UCHAR* data, int length)
1856 {
1857   // data is a pointer to the fileName string
1858
1859   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1860   int count = 4; // leave space for the packet length
1861
1862
1863   cMarks Marks;
1864   cRecordings Recordings;
1865   Recordings.Load(); // probably have to do this
1866
1867   cRecording *recording = Recordings.GetByName((char*)data);
1868
1869   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1870
1871   if (recording)
1872   {
1873     Marks.Load(recording->FileName());
1874     if (Marks.Count())
1875     {
1876       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1877       {
1878         log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1879
1880         if (count > 49000) break;
1881         *(ULONG*)&sendBuffer[count] = htonl(m->position);
1882         count += 4;
1883       }
1884     }
1885     else
1886     {
1887       log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1888       *(ULONG*)&sendBuffer[count] = htonl(0);
1889       count += 4;
1890     }
1891   }
1892
1893   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
1894
1895   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1896
1897   tcp.sendPacket(sendBuffer, count);
1898   delete[] sendBuffer;
1899   log->log("Client", Log::DEBUG, "Written Marks list");
1900
1901   return 1;
1902 }