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